lighthouse 9.5.0-dev.20230102 → 9.5.0-dev.20230103

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.
@@ -120,7 +120,7 @@ const mergeConfigFragment = _mergeConfigFragment;
120
120
  * Merge an array of items by a caller-defined key. `mergeConfigFragment` is used to merge any items
121
121
  * with a matching key.
122
122
  *
123
- * @template T
123
+ * @template {Record<string, any>} T
124
124
  * @param {Array<T>|null|undefined} baseArray
125
125
  * @param {Array<T>|null|undefined} extensionArray
126
126
  * @param {(item: T) => string} keyFn
@@ -238,7 +238,7 @@ function resolveFakeNavigations(artifactDefns, settings) {
238
238
  * @param {LH.Gatherer.GatherMode} gatherMode
239
239
  * @param {LH.Config.Json=} configJSON
240
240
  * @param {LH.Flags=} flags
241
- * @return {Promise<{config: LH.Config.FRConfig, warnings: string[]}>}
241
+ * @return {Promise<{resolvedConfig: LH.Config.FRConfig, warnings: string[]}>}
242
242
  */
243
243
  async function initializeConfig(gatherMode, configJSON, flags = {}) {
244
244
  const status = {msg: 'Initialize config', id: 'lh:config'};
@@ -257,7 +257,7 @@ async function initializeConfig(gatherMode, configJSON, flags = {}) {
257
257
  const navigations = resolveFakeNavigations(artifacts, settings);
258
258
 
259
259
  /** @type {LH.Config.FRConfig} */
260
- let config = {
260
+ let resolvedConfig = {
261
261
  artifacts,
262
262
  navigations,
263
263
  audits: await resolveAuditsToDefns(configWorkingCopy.audits, configDir),
@@ -266,25 +266,25 @@ async function initializeConfig(gatherMode, configJSON, flags = {}) {
266
266
  settings,
267
267
  };
268
268
 
269
- const {warnings} = assertValidConfig(config);
269
+ const {warnings} = assertValidConfig(resolvedConfig);
270
270
 
271
- config = filterConfigByGatherMode(config, gatherMode);
272
- config = filterConfigByExplicitFilters(config, settings);
271
+ resolvedConfig = filterConfigByGatherMode(resolvedConfig, gatherMode);
272
+ resolvedConfig = filterConfigByExplicitFilters(resolvedConfig, settings);
273
273
 
274
274
  log.timeEnd(status);
275
- return {config, warnings};
275
+ return {resolvedConfig, warnings};
276
276
  }
277
277
 
278
278
  /**
279
- * @param {LH.Config.FRConfig} config
279
+ * @param {LH.Config.FRConfig} resolvedConfig
280
280
  * @return {string}
281
281
  */
282
- function getConfigDisplayString(config) {
282
+ function getConfigDisplayString(resolvedConfig) {
283
283
  /** @type {LH.Config.FRConfig} */
284
- const jsonConfig = JSON.parse(JSON.stringify(config));
284
+ const resolvedConfigCopy = JSON.parse(JSON.stringify(resolvedConfig));
285
285
 
286
- if (jsonConfig.navigations) {
287
- for (const navigation of jsonConfig.navigations) {
286
+ if (resolvedConfigCopy.navigations) {
287
+ for (const navigation of resolvedConfigCopy.navigations) {
288
288
  for (let i = 0; i < navigation.artifacts.length; ++i) {
289
289
  // @ts-expect-error Breaking the Config.AnyArtifactDefn type.
290
290
  navigation.artifacts[i] = navigation.artifacts[i].id;
@@ -292,8 +292,8 @@ function getConfigDisplayString(config) {
292
292
  }
293
293
  }
294
294
 
295
- if (jsonConfig.artifacts) {
296
- for (const artifactDefn of jsonConfig.artifacts) {
295
+ if (resolvedConfigCopy.artifacts) {
296
+ for (const artifactDefn of resolvedConfigCopy.artifacts) {
297
297
  // @ts-expect-error Breaking the Config.AnyArtifactDefn type.
298
298
  artifactDefn.gatherer = artifactDefn.gatherer.path;
299
299
  // Dependencies are not declared on Config JSON
@@ -301,8 +301,8 @@ function getConfigDisplayString(config) {
301
301
  }
302
302
  }
303
303
 
304
- if (jsonConfig.audits) {
305
- for (const auditDefn of jsonConfig.audits) {
304
+ if (resolvedConfigCopy.audits) {
305
+ for (const auditDefn of resolvedConfigCopy.audits) {
306
306
  // @ts-expect-error Breaking the Config.AuditDefn type.
307
307
  auditDefn.implementation = undefined;
308
308
  if (Object.keys(auditDefn.options).length === 0) {
@@ -313,9 +313,9 @@ function getConfigDisplayString(config) {
313
313
  }
314
314
 
315
315
  // Printed config is more useful with localized strings.
316
- format.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale);
316
+ format.replaceIcuMessages(resolvedConfigCopy, resolvedConfigCopy.settings.locale);
317
317
 
318
- return JSON.stringify(jsonConfig, null, 2);
318
+ return JSON.stringify(resolvedConfigCopy, null, 2);
319
319
  }
320
320
 
321
321
  export {
@@ -257,19 +257,19 @@ function filterCategoriesByAvailableAudits(categories, availableAudits) {
257
257
  /**
258
258
  * Filters a config's artifacts, audits, and categories down to the set that supports the specified gather mode.
259
259
  *
260
- * @param {LH.Config.FRConfig} config
260
+ * @param {LH.Config.FRConfig} resolvedConfig
261
261
  * @param {LH.Gatherer.GatherMode} mode
262
262
  * @return {LH.Config.FRConfig}
263
263
  */
264
- function filterConfigByGatherMode(config, mode) {
265
- const artifacts = filterArtifactsByGatherMode(config.artifacts, mode);
266
- const supportedAudits = filterAuditsByGatherMode(config.audits, mode);
264
+ function filterConfigByGatherMode(resolvedConfig, mode) {
265
+ const artifacts = filterArtifactsByGatherMode(resolvedConfig.artifacts, mode);
266
+ const supportedAudits = filterAuditsByGatherMode(resolvedConfig.audits, mode);
267
267
  const audits = filterAuditsByAvailableArtifacts(supportedAudits, artifacts || []);
268
- const supportedCategories = filterCategoriesByGatherMode(config.categories, mode);
268
+ const supportedCategories = filterCategoriesByGatherMode(resolvedConfig.categories, mode);
269
269
  const categories = filterCategoriesByAvailableAudits(supportedCategories, audits || []);
270
270
 
271
271
  return {
272
- ...config,
272
+ ...resolvedConfig,
273
273
  artifacts,
274
274
  audits,
275
275
  categories,
@@ -280,22 +280,22 @@ function filterConfigByGatherMode(config, mode) {
280
280
  * Filters a config's artifacts, audits, and categories down to the requested set.
281
281
  * Skip audits overrides inclusion via `onlyAudits`/`onlyCategories`.
282
282
  *
283
- * @param {LH.Config.FRConfig} config
283
+ * @param {LH.Config.FRConfig} resolvedConfig
284
284
  * @param {Pick<LH.Config.Settings, 'onlyAudits'|'onlyCategories'|'skipAudits'>} filters
285
285
  * @return {LH.Config.FRConfig}
286
286
  */
287
- function filterConfigByExplicitFilters(config, filters) {
287
+ function filterConfigByExplicitFilters(resolvedConfig, filters) {
288
288
  const {onlyAudits, onlyCategories, skipAudits} = filters;
289
289
 
290
- warnOnUnknownOnlyCategories(config.categories, onlyCategories);
290
+ warnOnUnknownOnlyCategories(resolvedConfig.categories, onlyCategories);
291
291
 
292
- let baseAuditIds = getAuditIdsInCategories(config.categories, undefined);
292
+ let baseAuditIds = getAuditIdsInCategories(resolvedConfig.categories, undefined);
293
293
  if (onlyCategories) {
294
- baseAuditIds = getAuditIdsInCategories(config.categories, onlyCategories);
294
+ baseAuditIds = getAuditIdsInCategories(resolvedConfig.categories, onlyCategories);
295
295
  } else if (onlyAudits) {
296
296
  baseAuditIds = new Set();
297
- } else if (!config.categories || !Object.keys(config.categories).length) {
298
- baseAuditIds = new Set(config.audits?.map(audit => audit.implementation.meta.id));
297
+ } else if (!resolvedConfig.categories || !Object.keys(resolvedConfig.categories).length) {
298
+ baseAuditIds = new Set(resolvedConfig.audits?.map(audit => audit.implementation.meta.id));
299
299
  }
300
300
 
301
301
  const auditIdsToKeep = new Set(
@@ -306,17 +306,19 @@ function filterConfigByExplicitFilters(config, filters) {
306
306
  ].filter(auditId => !skipAudits || !skipAudits.includes(auditId))
307
307
  );
308
308
 
309
- const audits = auditIdsToKeep.size && config.audits ?
310
- config.audits.filter(audit => auditIdsToKeep.has(audit.implementation.meta.id)) :
311
- config.audits;
309
+ const audits = auditIdsToKeep.size && resolvedConfig.audits ?
310
+ resolvedConfig.audits.filter(audit => auditIdsToKeep.has(audit.implementation.meta.id)) :
311
+ resolvedConfig.audits;
312
312
 
313
- const availableCategories = filterCategoriesByAvailableAudits(config.categories, audits || []);
313
+ const availableCategories =
314
+ filterCategoriesByAvailableAudits(resolvedConfig.categories, audits || []);
314
315
  const categories = filterCategoriesByExplicitFilters(availableCategories, onlyCategories);
315
- const artifacts = filterArtifactsByAvailableAudits(config.artifacts, audits);
316
- const navigations = filterNavigationsByAvailableArtifacts(config.navigations, artifacts || []);
316
+ const artifacts = filterArtifactsByAvailableAudits(resolvedConfig.artifacts, audits);
317
+ const navigations =
318
+ filterNavigationsByAvailableArtifacts(resolvedConfig.navigations, artifacts || []);
317
319
 
318
320
  return {
319
- ...config,
321
+ ...resolvedConfig,
320
322
  artifacts,
321
323
  navigations,
322
324
  audits,
@@ -247,22 +247,22 @@ function assertArtifactTopologicalOrder(navigations) {
247
247
  }
248
248
 
249
249
  /**
250
- * @param {LH.Config.FRConfig} config
250
+ * @param {LH.Config.FRConfig} resolvedConfig
251
251
  * @return {{warnings: string[]}}
252
252
  */
253
- function assertValidConfig(config) {
254
- const {warnings} = assertValidFRNavigations(config.navigations);
253
+ function assertValidConfig(resolvedConfig) {
254
+ const {warnings} = assertValidFRNavigations(resolvedConfig.navigations);
255
255
 
256
- for (const artifactDefn of config.artifacts || []) {
256
+ for (const artifactDefn of resolvedConfig.artifacts || []) {
257
257
  assertValidFRGatherer(artifactDefn.gatherer);
258
258
  }
259
259
 
260
- for (const auditDefn of config.audits || []) {
260
+ for (const auditDefn of resolvedConfig.audits || []) {
261
261
  assertValidAudit(auditDefn);
262
262
  }
263
263
 
264
- assertValidCategories(config.categories, config.audits, config.groups);
265
- assertValidSettings(config.settings);
264
+ assertValidCategories(resolvedConfig.categories, resolvedConfig.audits, resolvedConfig.groups);
265
+ assertValidSettings(resolvedConfig.settings);
266
266
  return {warnings};
267
267
  }
268
268
 
@@ -12,12 +12,12 @@ import {
12
12
  } from './driver/environment.js';
13
13
 
14
14
  /**
15
- * @param {LH.Config.FRConfig} config
15
+ * @param {LH.Config.FRConfig} resolvedConfig
16
16
  * @param {LH.Gatherer.FRTransitionalDriver} driver
17
17
  * @param {{gatherMode: LH.Gatherer.GatherMode}} context
18
18
  * @return {Promise<LH.BaseArtifacts>}
19
19
  */
20
- async function getBaseArtifacts(config, driver, context) {
20
+ async function getBaseArtifacts(resolvedConfig, driver, context) {
21
21
  const BenchmarkIndex = await getBenchmarkIndex(driver.executionContext);
22
22
  const {userAgent} = await getBrowserVersion(driver.defaultSession);
23
23
 
@@ -26,7 +26,7 @@ async function getBaseArtifacts(config, driver, context) {
26
26
  fetchTime: new Date().toJSON(),
27
27
  Timing: [],
28
28
  LighthouseRunWarnings: [],
29
- settings: config.settings,
29
+ settings: resolvedConfig.settings,
30
30
  // Environment artifacts that can always be computed.
31
31
  BenchmarkIndex,
32
32
  HostUserAgent: userAgent,
@@ -70,6 +70,7 @@ async function dismissJavaScriptDialogs(session) {
70
70
  }
71
71
 
72
72
  /**
73
+ * Reset the storage and warn if any stored data could be affecting the scores.
73
74
  * @param {LH.Gatherer.FRProtocolSession} session
74
75
  * @param {string} url
75
76
  * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
@@ -78,11 +79,14 @@ async function resetStorageForUrl(session, url) {
78
79
  /** @type {Array<LH.IcuMessage>} */
79
80
  const warnings = [];
80
81
 
81
- // Reset the storage and warn if there appears to be other important data.
82
- const warning = await storage.getImportantStorageWarning(session, url);
83
- if (warning) warnings.push(warning);
84
- await storage.clearDataForOrigin(session, url);
85
- await storage.clearBrowserCaches(session);
82
+ const importantStorageWarning = await storage.getImportantStorageWarning(session, url);
83
+ if (importantStorageWarning) warnings.push(importantStorageWarning);
84
+
85
+ const clearDataWarnings = await storage.clearDataForOrigin(session, url);
86
+ warnings.push(...clearDataWarnings);
87
+
88
+ const clearCacheWarnings = await storage.clearBrowserCaches(session);
89
+ warnings.push(...clearCacheWarnings);
86
90
 
87
91
  return {warnings};
88
92
  }
@@ -8,6 +8,7 @@ import log from 'lighthouse-logger';
8
8
 
9
9
  import * as i18n from '../../lib/i18n/i18n.js';
10
10
 
11
+ /* eslint-disable max-len */
11
12
  const UIStrings = {
12
13
  /**
13
14
  * @description A warning that previously-saved data may have affected the measured performance and instructions on how to avoid the problem. "locations" will be a list of possible types of data storage locations, e.g. "IndexedDB", "Local Storage", or "Web SQL".
@@ -22,7 +23,12 @@ const UIStrings = {
22
23
  `Audit this page in an incognito window to prevent those resources ` +
23
24
  `from affecting your scores.}
24
25
  }`,
26
+ /** A warning that the data in the browser cache may have affected the measured performance because the operation to clear the browser cache timed out. */
27
+ warningCacheTimeout: 'Clearing the browser cache timed out. Try auditing this page again and file a bug if the issue persists.',
28
+ /** A warning that the data on the page's origin may have affected the measured performance because the operation to clear the origin data timed out. */
29
+ warningOriginDataTimeout: 'Clearing the origin data timed out. Try auditing this page again and file a bug if the issue persists.',
25
30
  };
31
+ /* eslint-enable max-len */
26
32
 
27
33
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
28
34
 
@@ -30,12 +36,14 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
30
36
  /**
31
37
  * @param {LH.Gatherer.FRProtocolSession} session
32
38
  * @param {string} url
33
- * @return {Promise<void>}
39
+ * @return {Promise<LH.IcuMessage[]>}
34
40
  */
35
41
  async function clearDataForOrigin(session, url) {
36
42
  const status = {msg: 'Cleaning origin data', id: 'lh:storage:clearDataForOrigin'};
37
43
  log.time(status);
38
44
 
45
+ const warnings = [];
46
+
39
47
  const origin = new URL(url).origin;
40
48
 
41
49
  // Clear some types of storage.
@@ -62,12 +70,15 @@ async function clearDataForOrigin(session, url) {
62
70
  } catch (err) {
63
71
  if (/** @type {LH.LighthouseError} */ (err).code === 'PROTOCOL_TIMEOUT') {
64
72
  log.warn('Driver', 'clearDataForOrigin timed out');
73
+ warnings.push(str_(UIStrings.warningOriginDataTimeout));
65
74
  } else {
66
75
  throw err;
67
76
  }
68
77
  } finally {
69
78
  log.timeEnd(status);
70
79
  }
80
+
81
+ return warnings;
71
82
  }
72
83
 
73
84
  /**
@@ -102,19 +113,32 @@ async function getImportantStorageWarning(session, url) {
102
113
  /**
103
114
  * Clear the network cache on disk and in memory.
104
115
  * @param {LH.Gatherer.FRProtocolSession} session
105
- * @return {Promise<void>}
116
+ * @return {Promise<LH.IcuMessage[]>}
106
117
  */
107
118
  async function clearBrowserCaches(session) {
108
119
  const status = {msg: 'Cleaning browser cache', id: 'lh:storage:clearBrowserCaches'};
109
120
  log.time(status);
110
121
 
111
- // Wipe entire disk cache
112
- await session.sendCommand('Network.clearBrowserCache');
113
- // Toggle 'Disable Cache' to evict the memory cache
114
- await session.sendCommand('Network.setCacheDisabled', {cacheDisabled: true});
115
- await session.sendCommand('Network.setCacheDisabled', {cacheDisabled: false});
122
+ const warnings = [];
123
+
124
+ try {
125
+ // Wipe entire disk cache
126
+ await session.sendCommand('Network.clearBrowserCache');
127
+ // Toggle 'Disable Cache' to evict the memory cache
128
+ await session.sendCommand('Network.setCacheDisabled', {cacheDisabled: true});
129
+ await session.sendCommand('Network.setCacheDisabled', {cacheDisabled: false});
130
+ } catch (err) {
131
+ if (/** @type {LH.LighthouseError} */ (err).code === 'PROTOCOL_TIMEOUT') {
132
+ log.warn('Driver', 'clearBrowserCaches timed out');
133
+ warnings.push(str_(UIStrings.warningCacheTimeout));
134
+ } else {
135
+ throw err;
136
+ }
137
+ } finally {
138
+ log.timeEnd(status);
139
+ }
116
140
 
117
- log.timeEnd(status);
141
+ return warnings;
118
142
  }
119
143
 
120
144
  export {
@@ -29,7 +29,7 @@ import {NetworkRecords} from '../computed/network-records.js';
29
29
  * @typedef NavigationContext
30
30
  * @property {Driver} driver
31
31
  * @property {LH.Puppeteer.Page} page
32
- * @property {LH.Config.FRConfig} config
32
+ * @property {LH.Config.FRConfig} resolvedConfig
33
33
  * @property {LH.Config.NavigationDefn} navigation
34
34
  * @property {LH.NavigationRequestor} requestor
35
35
  * @property {LH.FRBaseArtifacts} baseArtifacts
@@ -42,20 +42,20 @@ const DEFAULT_HOSTNAME = '127.0.0.1';
42
42
  const DEFAULT_PORT = 9222;
43
43
 
44
44
  /**
45
- * @param {{driver: Driver, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor}} args
45
+ * @param {{driver: Driver, resolvedConfig: LH.Config.FRConfig, requestor: LH.NavigationRequestor}} args
46
46
  * @return {Promise<{baseArtifacts: LH.FRBaseArtifacts}>}
47
47
  */
48
- async function _setup({driver, config, requestor}) {
48
+ async function _setup({driver, resolvedConfig, requestor}) {
49
49
  await driver.connect();
50
50
 
51
51
  // We can't trigger the navigation through user interaction if we reset the page before starting.
52
- if (typeof requestor === 'string' && !config.settings.skipAboutBlank) {
52
+ if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) {
53
53
  await gotoURL(driver, defaultNavigationConfig.blankPage, {waitUntil: ['navigated']});
54
54
  }
55
55
 
56
- const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'navigation'});
56
+ const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'navigation'});
57
57
 
58
- await prepare.prepareTargetForNavigationMode(driver, config.settings);
58
+ await prepare.prepareTargetForNavigationMode(driver, resolvedConfig.settings);
59
59
 
60
60
  return {baseArtifacts};
61
61
  }
@@ -64,15 +64,15 @@ async function _setup({driver, config, requestor}) {
64
64
  * @param {NavigationContext} navigationContext
65
65
  * @return {Promise<{warnings: Array<LH.IcuMessage>}>}
66
66
  */
67
- async function _setupNavigation({requestor, driver, navigation, config}) {
67
+ async function _setupNavigation({requestor, driver, navigation, resolvedConfig}) {
68
68
  // We can't trigger the navigation through user interaction if we reset the page before starting.
69
- if (typeof requestor === 'string' && !config.settings.skipAboutBlank) {
69
+ if (typeof requestor === 'string' && !resolvedConfig.settings.skipAboutBlank) {
70
70
  await gotoURL(driver, navigation.blankPage, {...navigation, waitUntil: ['navigated']});
71
71
  }
72
72
 
73
73
  const {warnings} = await prepare.prepareTargetForIndividualNavigation(
74
74
  driver.defaultSession,
75
- config.settings,
75
+ resolvedConfig.settings,
76
76
  {
77
77
  ...navigation,
78
78
  requestor,
@@ -94,14 +94,14 @@ async function _cleanupNavigation({driver}) {
94
94
  * @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined, warnings: Array<LH.IcuMessage>}>}
95
95
  */
96
96
  async function _navigate(navigationContext) {
97
- const {driver, config, requestor} = navigationContext;
97
+ const {driver, resolvedConfig, requestor} = navigationContext;
98
98
 
99
99
  try {
100
100
  const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, {
101
101
  ...navigationContext.navigation,
102
- debugNavigation: config.settings.debugNavigation,
103
- maxWaitForFcp: config.settings.maxWaitForFcp,
104
- maxWaitForLoad: config.settings.maxWaitForLoad,
102
+ debugNavigation: resolvedConfig.settings.debugNavigation,
103
+ maxWaitForFcp: resolvedConfig.settings.maxWaitForFcp,
104
+ maxWaitForLoad: resolvedConfig.settings.maxWaitForLoad,
105
105
  waitUntil: navigationContext.navigation.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
106
106
  });
107
107
  return {requestedUrl, mainDocumentUrl, navigationError: undefined, warnings};
@@ -181,7 +181,7 @@ async function _computeNavigationResult(
181
181
  : navigationError;
182
182
 
183
183
  if (pageLoadError) {
184
- const locale = navigationContext.config.settings.locale;
184
+ const locale = navigationContext.resolvedConfig.settings.locale;
185
185
  const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage, locale);
186
186
  log.error('NavigationRunner', localizedMessage, navigateResult.requestedUrl);
187
187
 
@@ -223,7 +223,7 @@ async function _navigation(navigationContext) {
223
223
  artifactDefinitions: navigationContext.navigation.artifacts,
224
224
  artifactState,
225
225
  baseArtifacts: navigationContext.baseArtifacts,
226
- settings: navigationContext.config.settings,
226
+ settings: navigationContext.resolvedConfig.settings,
227
227
  };
228
228
 
229
229
  const setupResult = await _setupNavigation(navigationContext);
@@ -250,24 +250,33 @@ async function _navigation(navigationContext) {
250
250
  }
251
251
 
252
252
  /**
253
- * @param {{driver: Driver, page: LH.Puppeteer.Page, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']}} args
253
+ * @param {{driver: Driver, page: LH.Puppeteer.Page, resolvedConfig: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']}} args
254
254
  * @return {Promise<{artifacts: Partial<LH.FRArtifacts & LH.FRBaseArtifacts>}>}
255
255
  */
256
- async function _navigations({driver, page, config, requestor, baseArtifacts, computedCache}) {
257
- if (!config.navigations) throw new Error('No navigations configured');
256
+ async function _navigations(args) {
257
+ const {
258
+ driver,
259
+ page,
260
+ resolvedConfig,
261
+ requestor,
262
+ baseArtifacts,
263
+ computedCache,
264
+ } = args;
265
+
266
+ if (!resolvedConfig.navigations) throw new Error('No navigations configured');
258
267
 
259
268
  /** @type {Partial<LH.FRArtifacts & LH.FRBaseArtifacts>} */
260
269
  const artifacts = {};
261
270
  /** @type {Array<LH.IcuMessage>} */
262
271
  const LighthouseRunWarnings = [];
263
272
 
264
- for (const navigation of config.navigations) {
273
+ for (const navigation of resolvedConfig.navigations) {
265
274
  const navigationContext = {
266
275
  driver,
267
276
  page,
268
277
  navigation,
269
278
  requestor,
270
- config,
279
+ resolvedConfig,
271
280
  baseArtifacts,
272
281
  computedCache,
273
282
  };
@@ -290,10 +299,10 @@ async function _navigations({driver, page, config, requestor, baseArtifacts, com
290
299
  }
291
300
 
292
301
  /**
293
- * @param {{requestedUrl?: string, driver: Driver, config: LH.Config.FRConfig}} args
302
+ * @param {{requestedUrl?: string, driver: Driver, resolvedConfig: LH.Config.FRConfig}} args
294
303
  */
295
- async function _cleanup({requestedUrl, driver, config}) {
296
- const didResetStorage = !config.settings.disableStorageReset && requestedUrl;
304
+ async function _cleanup({requestedUrl, driver, resolvedConfig}) {
305
+ const didResetStorage = !resolvedConfig.settings.disableStorageReset && requestedUrl;
297
306
  if (didResetStorage) await storage.clearDataForOrigin(driver.defaultSession, requestedUrl);
298
307
 
299
308
  await driver.disconnect();
@@ -306,15 +315,15 @@ async function _cleanup({requestedUrl, driver, config}) {
306
315
  * @return {Promise<LH.Gatherer.FRGatherResult>}
307
316
  */
308
317
  async function navigationGather(page, requestor, options = {}) {
309
- const {flags = {}} = options;
318
+ const {flags = {}, config} = options;
310
319
  log.setLevel(flags.logLevel || 'error');
311
320
 
312
- const {config} = await initializeConfig('navigation', options.config, flags);
321
+ const {resolvedConfig} = await initializeConfig('navigation', config, flags);
313
322
  const computedCache = new Map();
314
323
 
315
324
  const isCallback = typeof requestor === 'function';
316
325
 
317
- const runnerOptions = {config, computedCache};
326
+ const runnerOptions = {resolvedConfig, computedCache};
318
327
  const artifacts = await Runner.gather(
319
328
  async () => {
320
329
  const normalizedRequestor = isCallback ? requestor : UrlUtils.normalizeUrl(requestor);
@@ -330,7 +339,7 @@ async function navigationGather(page, requestor, options = {}) {
330
339
  const driver = new Driver(page);
331
340
  const context = {
332
341
  driver,
333
- config,
342
+ resolvedConfig,
334
343
  requestor: normalizedRequestor,
335
344
  };
336
345
  const {baseArtifacts} = await _setup(context);
@@ -18,10 +18,10 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
18
18
  * @return {Promise<LH.Gatherer.FRGatherResult>}
19
19
  */
20
20
  async function snapshotGather(page, options = {}) {
21
- const {flags = {}} = options;
21
+ const {flags = {}, config} = options;
22
22
  log.setLevel(flags.logLevel || 'error');
23
23
 
24
- const {config} = await initializeConfig('snapshot', options.config, flags);
24
+ const {resolvedConfig} = await initializeConfig('snapshot', config, flags);
25
25
  const driver = new Driver(page);
26
26
  await driver.connect();
27
27
 
@@ -29,15 +29,16 @@ async function snapshotGather(page, options = {}) {
29
29
  const computedCache = new Map();
30
30
  const url = await driver.url();
31
31
 
32
- const runnerOptions = {config, computedCache};
32
+ const runnerOptions = {resolvedConfig, computedCache};
33
33
  const artifacts = await Runner.gather(
34
34
  async () => {
35
- const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'snapshot'});
35
+ const baseArtifacts =
36
+ await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'snapshot'});
36
37
  baseArtifacts.URL = {
37
38
  finalDisplayedUrl: url,
38
39
  };
39
40
 
40
- const artifactDefinitions = config.artifacts || [];
41
+ const artifactDefinitions = resolvedConfig.artifacts || [];
41
42
  const artifactState = getEmptyArtifactState();
42
43
  await collectPhaseArtifacts({
43
44
  phase: 'getArtifact',
@@ -48,7 +49,7 @@ async function snapshotGather(page, options = {}) {
48
49
  artifactDefinitions,
49
50
  artifactState,
50
51
  computedCache,
51
- settings: config.settings,
52
+ settings: resolvedConfig.settings,
52
53
  });
53
54
 
54
55
  await driver.disconnect();
@@ -19,17 +19,17 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
19
19
  * @return {Promise<{endTimespanGather(): Promise<LH.Gatherer.FRGatherResult>}>}
20
20
  */
21
21
  async function startTimespanGather(page, options = {}) {
22
- const {flags = {}} = options;
22
+ const {flags = {}, config} = options;
23
23
  log.setLevel(flags.logLevel || 'error');
24
24
 
25
- const {config} = await initializeConfig('timespan', options.config, flags);
25
+ const {resolvedConfig} = await initializeConfig('timespan', config, flags);
26
26
  const driver = new Driver(page);
27
27
  await driver.connect();
28
28
 
29
29
  /** @type {Map<string, LH.ArbitraryEqualityMap>} */
30
30
  const computedCache = new Map();
31
- const artifactDefinitions = config.artifacts || [];
32
- const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'timespan'});
31
+ const artifactDefinitions = resolvedConfig.artifacts || [];
32
+ const baseArtifacts = await getBaseArtifacts(resolvedConfig, driver, {gatherMode: 'timespan'});
33
33
  const artifactState = getEmptyArtifactState();
34
34
  /** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
35
35
  const phaseOptions = {
@@ -40,10 +40,10 @@ async function startTimespanGather(page, options = {}) {
40
40
  baseArtifacts,
41
41
  computedCache,
42
42
  gatherMode: 'timespan',
43
- settings: config.settings,
43
+ settings: resolvedConfig.settings,
44
44
  };
45
45
 
46
- await prepareTargetForTimespanMode(driver, config.settings);
46
+ await prepareTargetForTimespanMode(driver, resolvedConfig.settings);
47
47
  await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseOptions});
48
48
  await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseOptions});
49
49
 
@@ -51,7 +51,7 @@ async function startTimespanGather(page, options = {}) {
51
51
  async endTimespanGather() {
52
52
  const finalDisplayedUrl = await driver.url();
53
53
 
54
- const runnerOptions = {config, computedCache};
54
+ const runnerOptions = {resolvedConfig, computedCache};
55
55
  const artifacts = await Runner.gather(
56
56
  async () => {
57
57
  baseArtifacts.URL = {finalDisplayedUrl};
package/core/index.js CHANGED
@@ -63,9 +63,9 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
63
63
  flags.logLevel = flags.logLevel || 'error';
64
64
  log.setLevel(flags.logLevel);
65
65
 
66
- const config = await Config.fromJson(configJSON, flags);
66
+ const resolvedConfig = await Config.fromJson(configJSON, flags);
67
67
  const computedCache = new Map();
68
- const options = {config, computedCache};
68
+ const options = {resolvedConfig, computedCache};
69
69
  const connection = userConnection || new CriConnection(flags.port, flags.hostname);
70
70
 
71
71
  // kick off a lighthouse run
@@ -179,9 +179,9 @@ class Config {
179
179
 
180
180
  const audits = await Config.requireAudits(configJSON.audits, configDir);
181
181
 
182
- const config = new Config(configJSON, {settings, passes, audits});
182
+ const resolvedConfig = new Config(configJSON, {settings, passes, audits});
183
183
  log.timeEnd(status);
184
- return config;
184
+ return resolvedConfig;
185
185
  }
186
186
 
187
187
  /**
package/core/runner.js CHANGED
@@ -34,12 +34,12 @@ class Runner {
34
34
  /**
35
35
  * @template {LH.Config.Config | LH.Config.FRConfig} TConfig
36
36
  * @param {LH.Artifacts} artifacts
37
- * @param {{config: TConfig, driverMock?: Driver, computedCache: Map<string, ArbitraryEqualityMap>}} options
37
+ * @param {{resolvedConfig: TConfig, driverMock?: Driver, computedCache: Map<string, ArbitraryEqualityMap>}} options
38
38
  * @return {Promise<LH.RunnerResult|undefined>}
39
39
  */
40
40
  static async audit(artifacts, options) {
41
- const {config, computedCache} = options;
42
- const settings = config.settings;
41
+ const {resolvedConfig, computedCache} = options;
42
+ const settings = resolvedConfig.settings;
43
43
 
44
44
  try {
45
45
  const runnerStatus = {msg: 'Audit phase', id: 'lh:runner:audit'};
@@ -54,10 +54,10 @@ class Runner {
54
54
  // Potentially quit early
55
55
  if (settings.gatherMode && !settings.auditMode) return;
56
56
 
57
- if (!config.audits) {
57
+ if (!resolvedConfig.audits) {
58
58
  throw new Error('No audits to evaluate.');
59
59
  }
60
- const auditResultsById = await Runner._runAudits(settings, config.audits, artifacts,
60
+ const auditResultsById = await Runner._runAudits(settings, resolvedConfig.audits, artifacts,
61
61
  lighthouseRunWarnings, computedCache);
62
62
 
63
63
  // LHR construction phase
@@ -79,8 +79,8 @@ class Runner {
79
79
 
80
80
  /** @type {Record<string, LH.RawIcu<LH.Result.Category>>} */
81
81
  let categories = {};
82
- if (config.categories) {
83
- categories = ReportScoring.scoreAllCategories(config.categories, auditResultsById);
82
+ if (resolvedConfig.categories) {
83
+ categories = ReportScoring.scoreAllCategories(resolvedConfig.categories, auditResultsById);
84
84
  }
85
85
 
86
86
  log.timeEnd(resultsStatus);
@@ -108,7 +108,7 @@ class Runner {
108
108
  audits: auditResultsById,
109
109
  configSettings: settings,
110
110
  categories,
111
- categoryGroups: config.groups || undefined,
111
+ categoryGroups: resolvedConfig.groups || undefined,
112
112
  stackPacks: stackPacks.getStackPacks(artifacts.Stacks),
113
113
  timing: this._getTiming(artifacts),
114
114
  i18n: {
@@ -143,12 +143,12 @@ class Runner {
143
143
  * and -GA will run everything plus save artifacts and lhr to disk.
144
144
  *
145
145
  * @template {LH.Config.Config | LH.Config.FRConfig} TConfig
146
- * @param {(runnerData: {config: TConfig, driverMock?: Driver}) => Promise<LH.Artifacts>} gatherFn
147
- * @param {{config: TConfig, driverMock?: Driver, computedCache: Map<string, ArbitraryEqualityMap>}} options
146
+ * @param {(runnerData: {resolvedConfig: TConfig, driverMock?: Driver}) => Promise<LH.Artifacts>} gatherFn
147
+ * @param {{resolvedConfig: TConfig, driverMock?: Driver, computedCache: Map<string, ArbitraryEqualityMap>}} options
148
148
  * @return {Promise<LH.Artifacts>}
149
149
  */
150
150
  static async gather(gatherFn, options) {
151
- const settings = options.config.settings;
151
+ const settings = options.resolvedConfig.settings;
152
152
 
153
153
  // Either load saved artifacts from disk or from the browser.
154
154
  try {
@@ -170,7 +170,7 @@ class Runner {
170
170
  log.time(runnerStatus, 'verbose');
171
171
 
172
172
  artifacts = await gatherFn({
173
- config: options.config,
173
+ resolvedConfig: options.resolvedConfig,
174
174
  driverMock: options.driverMock,
175
175
  });
176
176
 
@@ -248,22 +248,22 @@ class Runner {
248
248
  /**
249
249
  * Establish connection, load page and collect all required artifacts
250
250
  * @param {string} requestedUrl
251
- * @param {{config: Config, computedCache: Map<string, ArbitraryEqualityMap>, driverMock?: Driver}} runnerOpts
251
+ * @param {{resolvedConfig: Config, computedCache: Map<string, ArbitraryEqualityMap>, driverMock?: Driver}} runnerOpts
252
252
  * @param {Connection} connection
253
253
  * @return {Promise<LH.Artifacts>}
254
254
  */
255
255
  static async _gatherArtifactsFromBrowser(requestedUrl, runnerOpts, connection) {
256
- if (!runnerOpts.config.passes) {
256
+ if (!runnerOpts.resolvedConfig.passes) {
257
257
  throw new Error('No browser artifacts are either provided or requested.');
258
258
  }
259
259
  const driver = runnerOpts.driverMock || new Driver(connection);
260
260
  const gatherOpts = {
261
261
  driver,
262
262
  requestedUrl,
263
- settings: runnerOpts.config.settings,
263
+ settings: runnerOpts.resolvedConfig.settings,
264
264
  computedCache: runnerOpts.computedCache,
265
265
  };
266
- const artifacts = await GatherRunner.run(runnerOpts.config.passes, gatherOpts);
266
+ const artifacts = await GatherRunner.run(runnerOpts.resolvedConfig.passes, gatherOpts);
267
267
  return artifacts;
268
268
  }
269
269
 
package/core/user-flow.js CHANGED
@@ -323,9 +323,9 @@ async function auditGatherSteps(gatherSteps, options) {
323
323
  // Step specific configs take precedence over a config for the entire flow.
324
324
  const configJson = options.config;
325
325
  const {gatherMode} = artifacts.GatherContext;
326
- const {config} = await initializeConfig(gatherMode, configJson, flags);
326
+ const {resolvedConfig} = await initializeConfig(gatherMode, configJson, flags);
327
327
  runnerOptions = {
328
- config,
328
+ resolvedConfig,
329
329
  computedCache: new Map(),
330
330
  };
331
331
  }
@@ -5286,7 +5286,8 @@ class TopbarFeatures {
5286
5286
  */
5287
5287
  _setUpCollapseDetailsAfterPrinting() {
5288
5288
  // FF and IE implement these old events.
5289
- if ('onbeforeprint' in self) {
5289
+ const supportsOldPrintEvents = 'onbeforeprint' in self;
5290
+ if (supportsOldPrintEvents) {
5290
5291
  self.addEventListener('afterprint', this.collapseAllDetails);
5291
5292
  } else {
5292
5293
  // Note: FF implements both window.onbeforeprint and media listeners. However,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "9.5.0-dev.20230102",
4
+ "version": "9.5.0-dev.20230103",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -127,8 +127,8 @@
127
127
  "@types/ws": "^7.0.0",
128
128
  "@types/yargs": "^17.0.8",
129
129
  "@types/yargs-parser": "^20.2.1",
130
- "@typescript-eslint/eslint-plugin": "^5.27.1",
131
- "@typescript-eslint/parser": "^5.27.1",
130
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
131
+ "@typescript-eslint/parser": "^5.48.0",
132
132
  "acorn": "^8.5.0",
133
133
  "angular": "^1.7.4",
134
134
  "archiver": "^3.0.0",
@@ -178,7 +178,7 @@
178
178
  "terser": "^5.3.8",
179
179
  "testdouble": "^3.16.5",
180
180
  "typed-query-selector": "^2.6.1",
181
- "typescript": "^4.7.3",
181
+ "typescript": "^4.9.4",
182
182
  "wait-for-expect": "^3.0.2",
183
183
  "webtreemap-cdt": "^3.2.1"
184
184
  },
@@ -240,7 +240,8 @@ export class TopbarFeatures {
240
240
  */
241
241
  _setUpCollapseDetailsAfterPrinting() {
242
242
  // FF and IE implement these old events.
243
- if ('onbeforeprint' in self) {
243
+ const supportsOldPrintEvents = 'onbeforeprint' in self;
244
+ if (supportsOldPrintEvents) {
244
245
  self.addEventListener('afterprint', this.collapseAllDetails);
245
246
  } else {
246
247
  // Note: FF implements both window.onbeforeprint and media listeners. However,
@@ -1679,9 +1679,15 @@
1679
1679
  "core/gather/driver/navigation.js | warningTimeout": {
1680
1680
  "message": "The page loaded too slowly to finish within the time limit. Results may be incomplete."
1681
1681
  },
1682
+ "core/gather/driver/storage.js | warningCacheTimeout": {
1683
+ "message": "Clearing the browser cache timed out. Try auditing this page again and file a bug if the issue persists."
1684
+ },
1682
1685
  "core/gather/driver/storage.js | warningData": {
1683
1686
  "message": "{locationCount, plural,\n =1 {There may be stored data affecting loading performance in this location: {locations}. Audit this page in an incognito window to prevent those resources from affecting your scores.}\n other {There may be stored data affecting loading performance in these locations: {locations}. Audit this page in an incognito window to prevent those resources from affecting your scores.}\n }"
1684
1687
  },
1688
+ "core/gather/driver/storage.js | warningOriginDataTimeout": {
1689
+ "message": "Clearing the origin data timed out. Try auditing this page again and file a bug if the issue persists."
1690
+ },
1685
1691
  "core/lib/bfcache-strings.js | appBanner": {
1686
1692
  "message": "Pages that requested an AppBanner are not currently eligible for back/forward cache."
1687
1693
  },
@@ -1679,9 +1679,15 @@
1679
1679
  "core/gather/driver/navigation.js | warningTimeout": {
1680
1680
  "message": "T̂h́ê ṕâǵê ĺôád̂éd̂ t́ôó ŝĺôẃl̂ý t̂ó f̂ín̂íŝh́ ŵít̂h́îń t̂h́ê t́îḿê ĺîḿît́. R̂éŝúl̂t́ŝ ḿâý b̂é îńĉóm̂ṕl̂ét̂é."
1681
1681
  },
1682
+ "core/gather/driver/storage.js | warningCacheTimeout": {
1683
+ "message": "Ĉĺêár̂ín̂ǵ t̂h́ê b́r̂óŵśêŕ ĉáĉh́ê t́îḿêd́ ôút̂. T́r̂ý âúd̂ít̂ín̂ǵ t̂h́îś p̂áĝé âǵâín̂ án̂d́ f̂íl̂é â b́ûǵ îf́ t̂h́ê íŝśûé p̂ér̂śîśt̂ś."
1684
+ },
1682
1685
  "core/gather/driver/storage.js | warningData": {
1683
1686
  "message": "{locationCount, plural,\n =1 {T̂h́êŕê ḿâý b̂é ŝt́ôŕêd́ d̂át̂á âf́f̂éĉt́îńĝ ĺôád̂ín̂ǵ p̂ér̂f́ôŕm̂án̂ćê ín̂ t́ĥíŝ ĺôćât́îón̂: {locations}. Áûd́ît́ t̂h́îś p̂áĝé îń âń îńĉóĝńît́ô ẃîńd̂óŵ t́ô ṕr̂év̂én̂t́ t̂h́ôśê ŕêśôúr̂ćêś f̂ŕôḿ âf́f̂éĉt́îńĝ ýôúr̂ śĉór̂éŝ.}\n other {T́ĥér̂é m̂áŷ b́ê śt̂ór̂éd̂ d́ât́â áf̂f́êćt̂ín̂ǵ l̂óâd́îńĝ ṕêŕf̂ór̂ḿâńĉé îń t̂h́êśê ĺôćât́îón̂ś: {locations}. Âúd̂ít̂ t́ĥíŝ ṕâǵê ín̂ án̂ ín̂ćôǵn̂ít̂ó ŵín̂d́ôẃ t̂ó p̂ŕêv́êńt̂ t́ĥóŝé r̂éŝóûŕĉéŝ f́r̂óm̂ áf̂f́êćt̂ín̂ǵ ŷóûŕ ŝćôŕêś.}\n }"
1684
1687
  },
1688
+ "core/gather/driver/storage.js | warningOriginDataTimeout": {
1689
+ "message": "Ĉĺêár̂ín̂ǵ t̂h́ê ór̂íĝín̂ d́ât́â t́îḿêd́ ôút̂. T́r̂ý âúd̂ít̂ín̂ǵ t̂h́îś p̂áĝé âǵâín̂ án̂d́ f̂íl̂é â b́ûǵ îf́ t̂h́ê íŝśûé p̂ér̂śîśt̂ś."
1690
+ },
1685
1691
  "core/lib/bfcache-strings.js | appBanner": {
1686
1692
  "message": "P̂áĝéŝ t́ĥát̂ ŕêq́ûéŝt́êd́ âń Âṕp̂B́âńn̂ér̂ ár̂é n̂ót̂ ćûŕr̂én̂t́l̂ý êĺîǵîb́l̂é f̂ór̂ b́âćk̂/f́ôŕŵár̂d́ ĉáĉh́ê."
1687
1693
  },
@@ -67,7 +67,7 @@ declare module Gatherer {
67
67
  interface FRGatherResult {
68
68
  artifacts: Artifacts;
69
69
  runnerOptions: {
70
- config: Config.FRConfig;
70
+ resolvedConfig: Config.FRConfig;
71
71
  computedCache: Map<string, ArbitraryEqualityMap>
72
72
  }
73
73
  }