lighthouse 9.5.0-dev.20220904 → 9.5.0-dev.20220907

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 (51) hide show
  1. package/cli/cli-flags.js +17 -0
  2. package/cli/run.js +2 -2
  3. package/cli/test/smokehouse/report-assert.js +0 -4
  4. package/cli/test/smokehouse/smokehouse.js +6 -0
  5. package/core/audits/byte-efficiency/modern-image-formats.js +5 -5
  6. package/core/audits/byte-efficiency/offscreen-images.js +2 -2
  7. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -2
  8. package/core/audits/byte-efficiency/uses-optimized-images.js +5 -5
  9. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +2 -2
  10. package/core/audits/byte-efficiency/uses-responsive-images.js +2 -2
  11. package/core/audits/byte-efficiency/uses-text-compression.js +2 -2
  12. package/core/audits/dobetterweb/uses-http2.js +2 -2
  13. package/core/audits/font-display.js +7 -6
  14. package/core/audits/image-aspect-ratio.js +3 -3
  15. package/core/audits/image-size-responsive.js +3 -3
  16. package/core/audits/is-on-https.js +2 -2
  17. package/core/audits/network-requests.js +2 -2
  18. package/core/audits/seo/canonical.js +2 -2
  19. package/core/audits/seo/is-crawlable.js +0 -1
  20. package/core/audits/seo/link-text.js +2 -2
  21. package/core/audits/seo/plugins.js +0 -1
  22. package/core/audits/seo/robots-txt.js +0 -1
  23. package/core/audits/service-worker.js +0 -1
  24. package/core/audits/unsized-images.js +4 -4
  25. package/core/audits/uses-rel-preconnect.js +3 -2
  26. package/core/audits/uses-rel-preload.js +2 -2
  27. package/core/computed/image-records.js +2 -2
  28. package/core/computed/resource-summary.js +0 -1
  29. package/core/fraggle-rock/gather/navigation-runner.js +8 -5
  30. package/core/fraggle-rock/gather/runner-helpers.js +3 -0
  31. package/core/fraggle-rock/gather/snapshot-runner.js +3 -2
  32. package/core/fraggle-rock/gather/timespan-runner.js +3 -2
  33. package/core/gather/driver/navigation.js +3 -3
  34. package/core/gather/driver/network-monitor.js +2 -2
  35. package/core/gather/gather-runner.js +2 -2
  36. package/core/gather/gatherers/dobetterweb/optimized-images.js +2 -2
  37. package/core/gather/gatherers/dobetterweb/response-compression.js +2 -2
  38. package/core/gather/gatherers/link-elements.js +0 -1
  39. package/core/gather/gatherers/source-maps.js +0 -1
  40. package/core/index.js +2 -2
  41. package/core/lib/dependency-graph/simulator/network-analyzer.js +2 -2
  42. package/core/lib/icons.js +0 -2
  43. package/core/lib/manifest-parser.js +0 -2
  44. package/core/lib/network-request.js +7 -7
  45. package/core/lib/{url-shim.js → url-utils.js} +4 -12
  46. package/package.json +1 -2
  47. package/report/test/renderer/performance-category-renderer-test.js +0 -1
  48. package/report/test/renderer/report-renderer-test.js +0 -1
  49. package/tsconfig.json +1 -1
  50. package/types/gatherer.d.ts +2 -0
  51. package/core/fraggle-rock/replay/stringify-extension.js +0 -104
package/cli/cli-flags.js CHANGED
@@ -7,6 +7,7 @@
7
7
  /* eslint-disable max-len */
8
8
 
9
9
  import fs from 'fs';
10
+ import path from 'path';
10
11
 
11
12
  import yargs from 'yargs';
12
13
  import * as yargsHelpers from 'yargs/helpers';
@@ -227,6 +228,7 @@ function getYargsParser(manualArgv) {
227
228
  },
228
229
  'output-path': {
229
230
  type: 'string',
231
+ coerce: coerceOutputPath,
230
232
  describe: `The file path to output the results. Use 'stdout' to write to stdout.
231
233
  If using JSON output, default is stdout.
232
234
  If using HTML or CSV output, default is a file in the working directory with a name based on the test URL and date.
@@ -408,6 +410,21 @@ function coerceOutput(values) {
408
410
  return validValues;
409
411
  }
410
412
 
413
+ /**
414
+ * Verifies outputPath is something we can actually write to.
415
+ * @param {unknown=} value
416
+ * @return {string=}
417
+ */
418
+ function coerceOutputPath(value) {
419
+ if (value === undefined) return;
420
+
421
+ if (typeof value !== 'string' || !value || !fs.existsSync(path.dirname(value))) {
422
+ throw new Error(`--output-path (${value}) cannot be written to`);
423
+ }
424
+
425
+ return value;
426
+ }
427
+
411
428
  /**
412
429
  * Verifies value is a string, then coerces type to LH.Locale for convenience. However, don't
413
430
  * allowlist specific locales. Why? So we can support the user who requests 'es-MX' (unsupported)
package/cli/run.js CHANGED
@@ -19,7 +19,7 @@ import * as Printer from './printer.js';
19
19
  import lighthouse, {legacyNavigation} from '../core/index.js';
20
20
  import {getLhrFilenamePrefix} from '../report/generator/file-namer.js';
21
21
  import * as assetSaver from '../core/lib/asset-saver.js';
22
- import URL from '../core/lib/url-shim.js';
22
+ import UrlUtils from '../core/lib/url-utils.js';
23
23
 
24
24
  /** @typedef {Error & {code: string, friendlyMessage?: string}} ExitError */
25
25
 
@@ -232,7 +232,7 @@ async function runLighthouse(url, flags, config) {
232
232
  }
233
233
 
234
234
  const shouldGather = flags.gatherMode || flags.gatherMode === flags.auditMode;
235
- const shouldUseLocalChrome = URL.isLikeLocalhost(flags.hostname);
235
+ const shouldUseLocalChrome = UrlUtils.isLikeLocalhost(flags.hostname);
236
236
  if (shouldGather && shouldUseLocalChrome) {
237
237
  launchedChrome = await getDebuggableChrome(flags);
238
238
  flags.port = launchedChrome.port;
@@ -259,10 +259,6 @@ function pruneExpectations(localConsole, lhr, expected, reportOptions) {
259
259
  * @param {*} obj
260
260
  */
261
261
  function failsChromeVersionCheck(obj) {
262
- // LHR never actually run, so we can't know. Better to not prune
263
- // things than to fail loudly when accessing lhr.environment.
264
- if (!lhr.environment) return false;
265
-
266
262
  return !chromiumVersionCheck({
267
263
  version: getChromeVersionString(),
268
264
  min: obj._minChromiumVersion,
@@ -195,6 +195,12 @@ async function runSmokeTest(smokeTestDefn, testOptions) {
195
195
  ...await lighthouseRunner(requestedUrl, configJson, {isDebug, useLegacyNavigation}),
196
196
  networkRequests: takeNetworkRequestUrls ? takeNetworkRequestUrls() : undefined,
197
197
  };
198
+
199
+ if (!result.lhr?.audits || !result.artifacts) {
200
+ // Something went really wrong and the runner didn't catch it.
201
+ throw new Error('lighthouse runner returned a bad result. got lhr:\n' +
202
+ JSON.stringify(result.lhr, null, 2));
203
+ }
198
204
  } catch (e) {
199
205
  // Clear the network requests so that when we retry, we don't see duplicates.
200
206
  if (takeNetworkRequestUrls) takeNetworkRequestUrls();
@@ -9,7 +9,7 @@
9
9
 
10
10
 
11
11
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
12
- import URL from '../../lib/url-shim.js';
12
+ import UrlUtils from '../../lib/url-utils.js';
13
13
  import * as i18n from '../../lib/i18n/i18n.js';
14
14
 
15
15
  const UIStrings = {
@@ -106,7 +106,7 @@ class ModernImageFormats extends ByteEfficiencyAudit {
106
106
  const imageElement = imageElementsByURL.get(image.url);
107
107
 
108
108
  if (image.failed) {
109
- warnings.push(`Unable to decode ${URL.getURLDisplayName(image.url)}`);
109
+ warnings.push(`Unable to decode ${UrlUtils.getURLDisplayName(image.url)}`);
110
110
  continue;
111
111
  }
112
112
 
@@ -123,7 +123,7 @@ class ModernImageFormats extends ByteEfficiencyAudit {
123
123
 
124
124
  if (typeof webpSize === 'undefined') {
125
125
  if (!imageElement) {
126
- warnings.push(`Unable to locate resource ${URL.getURLDisplayName(image.url)}`);
126
+ warnings.push(`Unable to locate resource ${UrlUtils.getURLDisplayName(image.url)}`);
127
127
  continue;
128
128
  }
129
129
 
@@ -152,8 +152,8 @@ class ModernImageFormats extends ByteEfficiencyAudit {
152
152
  const wastedBytes = image.originalSize - avifSize;
153
153
  if (wastedBytes < IGNORE_THRESHOLD_IN_BYTES) continue;
154
154
 
155
- const url = URL.elideDataURI(image.url);
156
- const isCrossOrigin = !URL.originsMatch(pageURL, image.url);
155
+ const url = UrlUtils.elideDataURI(image.url);
156
+ const isCrossOrigin = !UrlUtils.originsMatch(pageURL, image.url);
157
157
 
158
158
  items.push({
159
159
  node: imageElement ? ByteEfficiencyAudit.makeNodeItem(imageElement.node) : undefined,
@@ -12,7 +12,7 @@
12
12
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
13
13
  import {NetworkRequest} from '../../lib/network-request.js';
14
14
  import {Sentry} from '../../lib/sentry.js';
15
- import URL from '../../lib/url-shim.js';
15
+ import UrlUtils from '../../lib/url-utils.js';
16
16
  import * as i18n from '../../lib/i18n/i18n.js';
17
17
  import Interactive from '../../computed/metrics/interactive.js';
18
18
  import ProcessedTrace from '../../computed/processed-trace.js';
@@ -87,7 +87,7 @@ class OffscreenImages extends ByteEfficiencyAudit {
87
87
  // If the image had its loading behavior explicitly controlled already, treat it as passed.
88
88
  if (image.loading === 'lazy' || image.loading === 'eager') return null;
89
89
 
90
- const url = URL.elideDataURI(image.src);
90
+ const url = UrlUtils.elideDataURI(image.src);
91
91
  const totalPixels = image.displayedWidth * image.displayedHeight;
92
92
  const visiblePixels = this.computeVisiblePixels(image.clientRect, viewportDimensions);
93
93
  // Treat images with 0 area as if they're offscreen. See https://github.com/GoogleChrome/lighthouse/issues/1914
@@ -8,7 +8,7 @@ import parseCacheControl from 'parse-cache-control';
8
8
 
9
9
  import {Audit} from '../audit.js';
10
10
  import {NetworkRequest} from '../../lib/network-request.js';
11
- import URL from '../../lib/url-shim.js';
11
+ import UrlUtils from '../../lib/url-utils.js';
12
12
  import {linearInterpolation} from '../../lib/statistics.js';
13
13
  import * as i18n from '../../lib/i18n/i18n.js';
14
14
  import NetworkRecords from '../../computed/network-records.js';
@@ -233,7 +233,7 @@ class CacheHeaders extends Audit {
233
233
  const cacheHitProbability = CacheHeaders.getCacheHitProbability(cacheLifetimeInSeconds);
234
234
  if (cacheHitProbability > IGNORE_THRESHOLD_IN_PERCENT) continue;
235
235
 
236
- const url = URL.elideDataURI(record.url);
236
+ const url = UrlUtils.elideDataURI(record.url);
237
237
  const totalBytes = record.transferSize || 0;
238
238
  const wastedBytes = (1 - cacheHitProbability) * totalBytes;
239
239
 
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
13
- import URL from '../../lib/url-shim.js';
13
+ import UrlUtils from '../../lib/url-utils.js';
14
14
  import * as i18n from '../../lib/i18n/i18n.js';
15
15
 
16
16
  const UIStrings = {
@@ -83,7 +83,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
83
83
  const imageElement = imageElementsByURL.get(image.url);
84
84
 
85
85
  if (image.failed) {
86
- warnings.push(`Unable to decode ${URL.getURLDisplayName(image.url)}`);
86
+ warnings.push(`Unable to decode ${UrlUtils.getURLDisplayName(image.url)}`);
87
87
  continue;
88
88
  } else if (/(jpeg|bmp)/.test(image.mimeType) === false) {
89
89
  continue;
@@ -94,7 +94,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
94
94
 
95
95
  if (typeof jpegSize === 'undefined') {
96
96
  if (!imageElement) {
97
- warnings.push(`Unable to locate resource ${URL.getURLDisplayName(image.url)}`);
97
+ warnings.push(`Unable to locate resource ${UrlUtils.getURLDisplayName(image.url)}`);
98
98
  continue;
99
99
  }
100
100
 
@@ -111,8 +111,8 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
111
111
 
112
112
  if (image.originalSize < jpegSize + IGNORE_THRESHOLD_IN_BYTES) continue;
113
113
 
114
- const url = URL.elideDataURI(image.url);
115
- const isCrossOrigin = !URL.originsMatch(pageURL, image.url);
114
+ const url = UrlUtils.elideDataURI(image.url);
115
+ const isCrossOrigin = !UrlUtils.originsMatch(pageURL, image.url);
116
116
  const jpegSavings = UsesOptimizedImages.computeSavings({...image, jpegSize});
117
117
 
118
118
  items.push({
@@ -13,7 +13,7 @@
13
13
 
14
14
  import {Audit} from '../audit.js';
15
15
  import * as UsesResponsiveImages from './uses-responsive-images.js';
16
- import URL from '../../lib/url-shim.js';
16
+ import UrlUtils from '../../lib/url-utils.js';
17
17
  import * as i18n from '../../lib/i18n/i18n.js';
18
18
 
19
19
  const UIStrings = {
@@ -71,7 +71,7 @@ class UsesResponsiveImagesSnapshot extends Audit {
71
71
 
72
72
  items.push({
73
73
  node: Audit.makeNodeItem(image.node),
74
- url: URL.elideDataURI(image.src),
74
+ url: UrlUtils.elideDataURI(image.src),
75
75
  displayedDimensions: `${displayed.width}x${displayed.height}`,
76
76
  actualDimensions: `${actual.width}x${actual.height}`,
77
77
  });
@@ -16,7 +16,7 @@
16
16
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
17
17
  import {NetworkRequest} from '../../lib/network-request.js';
18
18
  import ImageRecords from '../../computed/image-records.js';
19
- import URL from '../../lib/url-shim.js';
19
+ import UrlUtils from '../../lib/url-utils.js';
20
20
  import * as i18n from '../../lib/i18n/i18n.js';
21
21
 
22
22
  const UIStrings = {
@@ -100,7 +100,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
100
100
  const displayed = this.getDisplayedDimensions(image, ViewportDimensions);
101
101
  const usedPixels = displayed.width * displayed.height;
102
102
 
103
- const url = URL.elideDataURI(image.src);
103
+ const url = UrlUtils.elideDataURI(image.src);
104
104
  const actualPixels = image.naturalWidth * image.naturalHeight;
105
105
  const wastedRatio = 1 - (usedPixels / actualPixels);
106
106
  const totalBytes = NetworkRequest.getResourceSizeOnNetwork(networkRecord);
@@ -10,7 +10,7 @@
10
10
 
11
11
 
12
12
  import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
13
- import URL from '../../lib/url-shim.js';
13
+ import UrlUtils from '../../lib/url-utils.js';
14
14
  import * as i18n from '../../lib/i18n/i18n.js';
15
15
 
16
16
  const UIStrings = {
@@ -68,7 +68,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
68
68
  }
69
69
 
70
70
  // remove duplicates
71
- const url = URL.elideDataURI(record.url);
71
+ const url = UrlUtils.elideDataURI(record.url);
72
72
  const isDuplicate = items.find(item => item.url === url &&
73
73
  item.totalBytes === record.resourceSize);
74
74
  if (isDuplicate) {
@@ -14,7 +14,7 @@
14
14
 
15
15
  import {Audit} from '../audit.js';
16
16
  import ThirdParty from '../../lib/third-party-web.js';
17
- import URL from '../../lib/url-shim.js';
17
+ import UrlUtils from '../../lib/url-utils.js';
18
18
  import {ByteEfficiencyAudit} from '../byte-efficiency/byte-efficiency-audit.js';
19
19
  import Interactive from '../../computed/metrics/lantern-interactive.js';
20
20
  import {NetworkRequest} from '../../lib/network-request.js';
@@ -167,7 +167,7 @@ class UsesHTTP2Audit extends Audit {
167
167
  const groupedByOrigin = new Map();
168
168
  for (const record of networkRecords) {
169
169
  if (!UsesHTTP2Audit.isStaticAsset(record)) continue;
170
- if (URL.isLikeLocalhost(record.parsedURL.host)) continue;
170
+ if (UrlUtils.isLikeLocalhost(record.parsedURL.host)) continue;
171
171
  const existing = groupedByOrigin.get(record.parsedURL.securityOrigin) || [];
172
172
  existing.push(record);
173
173
  groupedByOrigin.set(record.parsedURL.securityOrigin, existing);
@@ -5,14 +5,15 @@
5
5
  */
6
6
 
7
7
  import {Audit} from './audit.js';
8
- import URL from '../lib/url-shim.js';
9
- const PASSING_FONT_DISPLAY_REGEX = /^(block|fallback|optional|swap)$/;
10
- const CSS_URL_REGEX = /url\((.*?)\)/;
11
- const CSS_URL_GLOBAL_REGEX = new RegExp(CSS_URL_REGEX, 'g');
8
+ import UrlUtils from '../lib/url-utils.js';
12
9
  import * as i18n from '../lib/i18n/i18n.js';
13
10
  import {Sentry} from '../lib/sentry.js';
14
11
  import NetworkRecords from '../computed/network-records.js';
15
12
 
13
+ const PASSING_FONT_DISPLAY_REGEX = /^(block|fallback|optional|swap)$/;
14
+ const CSS_URL_REGEX = /url\((.*?)\)/;
15
+ const CSS_URL_GLOBAL_REGEX = new RegExp(CSS_URL_REGEX, 'g');
16
+
16
17
  const UIStrings = {
17
18
  /** Title of a diagnostic audit that provides detail on if all the text on a webpage was visible while the page was loading its webfonts. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
18
19
  title: 'All text remains visible during webfont loads',
@@ -98,7 +99,7 @@ class FontDisplay extends Audit {
98
99
  // Convert the relative CSS URL to an absolute URL and add it to the target set.
99
100
  for (const relativeURL of relativeURLs) {
100
101
  try {
101
- const relativeRoot = URL.isValid(stylesheet.header.sourceURL) ?
102
+ const relativeRoot = UrlUtils.isValid(stylesheet.header.sourceURL) ?
102
103
  stylesheet.header.sourceURL : artifacts.URL.finalUrl;
103
104
  const absoluteURL = new URL(relativeURL, relativeRoot);
104
105
  targetURLSet.add(absoluteURL.href);
@@ -121,7 +122,7 @@ class FontDisplay extends Audit {
121
122
  /** @type {Map<string, number>} */
122
123
  const warningCountByOrigin = new Map();
123
124
  for (const warningUrl of warningUrls) {
124
- const origin = URL.getOrigin(warningUrl);
125
+ const origin = UrlUtils.getOrigin(warningUrl);
125
126
  if (!origin) continue;
126
127
 
127
128
  const count = warningCountByOrigin.get(origin) || 0;
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  import {Audit} from './audit.js';
15
- import URL from '../lib/url-shim.js';
15
+ import UrlUtils from '../lib/url-utils.js';
16
16
  import * as i18n from '../lib/i18n/i18n.js';
17
17
 
18
18
  const UIStrings = {
@@ -54,7 +54,7 @@ class ImageAspectRatio extends Audit {
54
54
  * @return {{url: string, node: LH.Audit.Details.NodeValue, displayedAspectRatio: string, actualAspectRatio: string, doRatiosMatch: boolean}}
55
55
  */
56
56
  static computeAspectRatios(image) {
57
- const url = URL.elideDataURI(image.src);
57
+ const url = UrlUtils.elideDataURI(image.src);
58
58
  const actualAspectRatio = image.naturalDimensions.width / image.naturalDimensions.height;
59
59
  const displayedAspectRatio = image.displayedWidth / image.displayedHeight;
60
60
 
@@ -89,7 +89,7 @@ class ImageAspectRatio extends Audit {
89
89
  // - filter all svgs as they have no natural dimensions to audit
90
90
  // - filter out images that have falsy naturalWidth or naturalHeight
91
91
  return !image.isCss &&
92
- URL.guessMimeType(image.src) !== 'image/svg+xml' &&
92
+ UrlUtils.guessMimeType(image.src) !== 'image/svg+xml' &&
93
93
  image.naturalDimensions &&
94
94
  image.naturalDimensions.height > 5 &&
95
95
  image.naturalDimensions.width > 5 &&
@@ -11,7 +11,7 @@
11
11
 
12
12
 
13
13
  import {Audit} from './audit.js';
14
- import URL from '../lib/url-shim.js';
14
+ import UrlUtils from '../lib/url-utils.js';
15
15
  import * as i18n from '../lib/i18n/i18n.js';
16
16
 
17
17
  /** @typedef {LH.Artifacts.ImageElement & Required<Pick<LH.Artifacts.ImageElement, 'naturalDimensions'>>} ImageWithNaturalDimensions */
@@ -96,7 +96,7 @@ function isCandidate(image) {
96
96
  ) {
97
97
  return false;
98
98
  }
99
- if (URL.guessMimeType(image.src) === 'image/svg+xml') {
99
+ if (UrlUtils.guessMimeType(image.src) === 'image/svg+xml') {
100
100
  return false;
101
101
  }
102
102
  if (image.isCss) {
@@ -147,7 +147,7 @@ function getResult(image, DPR) {
147
147
  const [expectedWidth, expectedHeight] =
148
148
  expectedImageSize(image.displayedWidth, image.displayedHeight, DPR);
149
149
  return {
150
- url: URL.elideDataURI(image.src),
150
+ url: UrlUtils.elideDataURI(image.src),
151
151
  node: Audit.makeNodeItem(image.node),
152
152
  displayedSize: `${image.displayedWidth} x ${image.displayedHeight}`,
153
153
  actualSize: `${image.naturalDimensions.width} x ${image.naturalDimensions.height}`,
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {Audit} from './audit.js';
8
- import URL from '../lib/url-shim.js';
8
+ import UrlUtils from '../lib/url-utils.js';
9
9
  import {NetworkRequest} from '../lib/network-request.js';
10
10
  import NetworkRecords from '../computed/network-records.js';
11
11
  import * as i18n from '../lib/i18n/i18n.js';
@@ -75,7 +75,7 @@ class HTTPS extends Audit {
75
75
  return NetworkRecords.request(devtoolsLogs, context).then(networkRecords => {
76
76
  const insecureURLs = networkRecords
77
77
  .filter(record => !NetworkRequest.isSecureRequest(record))
78
- .map(record => URL.elideDataURI(record.url));
78
+ .map(record => UrlUtils.elideDataURI(record.url));
79
79
 
80
80
  /** @type {Array<{url: string, resolution?: LH.IcuMessage|string}>} */
81
81
  const items = Array.from(new Set(insecureURLs)).map(url => ({url, resolution: undefined}));
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {Audit} from './audit.js';
8
- import URL from '../lib/url-shim.js';
8
+ import UrlUtils from '../lib/url-utils.js';
9
9
  import NetworkRecords from '../computed/network-records.js';
10
10
  import MainResource from '../computed/main-resource.js';
11
11
 
@@ -62,7 +62,7 @@ class NetworkRequests extends Audit {
62
62
  undefined;
63
63
 
64
64
  return {
65
- url: URL.elideDataURI(record.url),
65
+ url: UrlUtils.elideDataURI(record.url),
66
66
  protocol: record.protocol,
67
67
  startTime: timeToMs(record.startTime),
68
68
  endTime: timeToMs(record.endTime),
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {Audit} from '../audit.js';
8
- import URL from '../../lib/url-shim.js';
8
+ import UrlUtils from '../../lib/url-utils.js';
9
9
  import MainResource from '../../computed/main-resource.js';
10
10
  import * as i18n from '../../lib/i18n/i18n.js';
11
11
 
@@ -93,7 +93,7 @@ class Canonical extends Audit {
93
93
  // Links that had an hrefRaw but didn't have a valid href were invalid, flag them
94
94
  if (!link.href) invalidCanonicalLink = link;
95
95
  // Links that had a valid href but didn't have a valid hrefRaw must have been relatively resolved, flag them
96
- else if (!URL.isValid(link.hrefRaw)) relativeCanonicallink = link;
96
+ else if (!UrlUtils.isValid(link.hrefRaw)) relativeCanonicallink = link;
97
97
  // Otherwise, it was a valid canonical URL
98
98
  else uniqueCanonicalURLs.add(link.href);
99
99
  } else if (link.rel === 'alternate') {
@@ -7,7 +7,6 @@
7
7
  import robotsParser from 'robots-parser';
8
8
 
9
9
  import {Audit} from '../audit.js';
10
- import URL from '../../lib/url-shim.js';
11
10
  import MainResource from '../../computed/main-resource.js';
12
11
  import * as i18n from '../../lib/i18n/i18n.js';
13
12
 
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import {Audit} from '../audit.js';
8
- import URL from '../../lib/url-shim.js';
8
+ import UrlUtils from '../../lib/url-utils.js';
9
9
  import * as i18n from '../../lib/i18n/i18n.js';
10
10
 
11
11
  const BLOCKLIST = new Set([
@@ -121,7 +121,7 @@ class LinkText extends Audit {
121
121
  href.startsWith('mailto:') ||
122
122
  // This line prevents the audit from flagging anchor links.
123
123
  // In this case it is better to use `finalUrl` than `mainDocumentUrl`.
124
- URL.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
124
+ UrlUtils.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
125
125
  ) {
126
126
  return false;
127
127
  }
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import {Audit} from '../audit.js';
8
- import URL from '../../lib/url-shim.js';
9
8
  import * as i18n from '../../lib/i18n/i18n.js';
10
9
 
11
10
  const JAVA_APPLET_TYPE = 'application/x-java-applet';
@@ -12,7 +12,6 @@
12
12
  */
13
13
 
14
14
  import {Audit} from '../audit.js';
15
- import URL from '../../lib/url-shim.js';
16
15
  import * as i18n from '../../lib/i18n/i18n.js';
17
16
 
18
17
  const HTTP_CLIENT_ERROR_CODE_LOW = 400;
@@ -4,7 +4,6 @@
4
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
5
  */
6
6
 
7
- import URL from '../lib/url-shim.js';
8
7
  import {Audit} from './audit.js';
9
8
  import * as i18n from '../lib/i18n/i18n.js';
10
9
 
@@ -11,7 +11,7 @@
11
11
 
12
12
  import {Audit} from './audit.js';
13
13
  import * as i18n from './../lib/i18n/i18n.js';
14
- import URL from './../lib/url-shim.js';
14
+ import UrlUtils from '../lib/url-utils.js';
15
15
 
16
16
  const UIStrings = {
17
17
  /** Title of a Lighthouse audit that provides detail on whether all images have explicit width and height. This descriptive title is shown to users when every image has explicit width and height */
@@ -105,9 +105,9 @@ class UnsizedImages extends Audit {
105
105
  * @return {boolean}
106
106
  */
107
107
  static isNonNetworkSvg(image) {
108
- const isSvg = URL.guessMimeType(image.src) === 'image/svg+xml';
108
+ const isSvg = UrlUtils.guessMimeType(image.src) === 'image/svg+xml';
109
109
  const urlScheme = image.src.slice(0, image.src.indexOf(':'));
110
- const isNonNetwork = URL.isNonNetworkProtocol(urlScheme);
110
+ const isNonNetwork = UrlUtils.isNonNetworkProtocol(urlScheme);
111
111
  return isSvg && isNonNetwork;
112
112
  }
113
113
 
@@ -139,7 +139,7 @@ class UnsizedImages extends Audit {
139
139
  if (isNotDisplayed) continue;
140
140
 
141
141
  unsizedImages.push({
142
- url: URL.elideDataURI(image.src),
142
+ url: UrlUtils.elideDataURI(image.src),
143
143
  node: Audit.makeNodeItem(image.node),
144
144
  });
145
145
  }
@@ -6,7 +6,7 @@
6
6
 
7
7
  import {Audit} from './audit.js';
8
8
  import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
9
- import URL from '../lib/url-shim.js';
9
+ import UrlUtils from '../lib/url-utils.js';
10
10
  import * as i18n from '../lib/i18n/i18n.js';
11
11
  import NetworkRecords from '../computed/network-records.js';
12
12
  import MainResource from '../computed/main-resource.js';
@@ -162,7 +162,8 @@ class UsesRelPreconnectAudit extends Audit {
162
162
  });
163
163
 
164
164
  const preconnectLinks = artifacts.LinkElements.filter(el => el.rel === 'preconnect');
165
- const preconnectOrigins = new Set(preconnectLinks.map(link => URL.getOrigin(link.href || '')));
165
+ const preconnectOrigins =
166
+ new Set(preconnectLinks.map(link => UrlUtils.getOrigin(link.href || '')));
166
167
 
167
168
  /** @type {Array<{url: string, wastedMs: number}>}*/
168
169
  let results = [];
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- import URL from '../lib/url-shim.js';
7
+ import UrlUtils from '../lib/url-utils.js';
8
8
  import {NetworkRequest} from '../lib/network-request.js';
9
9
  import {Audit} from './audit.js';
10
10
  import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
@@ -128,7 +128,7 @@ class UsesRelPreloadAudit extends Audit {
128
128
  // It's not a request for the main frame, it wouldn't get reused even if you did preload it.
129
129
  if (request.frameId !== mainResource.frameId) return false;
130
130
  // We survived everything else, just check that it's a first party request.
131
- return URL.rootDomainsMatch(request.url, mainResource.url);
131
+ return UrlUtils.rootDomainsMatch(request.url, mainResource.url);
132
132
  }
133
133
 
134
134
  /**
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- import URL from '../lib/url-shim.js';
7
+ import UrlUtils from '../lib/url-utils.js';
8
8
  import {makeComputedArtifact} from './computed-artifact.js';
9
9
 
10
10
  class ImageRecords {
@@ -43,7 +43,7 @@ class ImageRecords {
43
43
  // Don't change the guessed mime type if no mime type was found.
44
44
  imageRecords.push({
45
45
  ...element,
46
- mimeType: mimeType ? mimeType : URL.guessMimeType(element.src),
46
+ mimeType: mimeType ? mimeType : UrlUtils.guessMimeType(element.src),
47
47
  });
48
48
  }
49
49
 
@@ -6,7 +6,6 @@
6
6
 
7
7
  import {makeComputedArtifact} from './computed-artifact.js';
8
8
  import NetworkRecords from './network-records.js';
9
- import URL from '../lib/url-shim.js';
10
9
  import {NetworkRequest} from '../lib/network-request.js';
11
10
  import {Budget} from '../config/budget.js';
12
11
  import {Util} from '../util.cjs';
@@ -19,7 +19,7 @@ import {initializeConfig} from '../config/config.js';
19
19
  import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
20
20
  import * as format from '../../../shared/localization/format.js';
21
21
  import {LighthouseError} from '../../lib/lh-error.js';
22
- import URL from '../../lib/url-shim.js';
22
+ import UrlUtils from '../../lib/url-utils.js';
23
23
  import {getPageLoadError} from '../../lib/navigation-error.js';
24
24
  import Trace from '../../gather/gatherers/trace.js';
25
25
  import DevtoolsLog from '../../gather/gatherers/devtools-log.js';
@@ -28,6 +28,7 @@ import NetworkRecords from '../../computed/network-records.js';
28
28
  /**
29
29
  * @typedef NavigationContext
30
30
  * @property {Driver} driver
31
+ * @property {LH.Puppeteer.Page} page
31
32
  * @property {LH.Config.FRConfig} config
32
33
  * @property {LH.Config.NavigationDefn} navigation
33
34
  * @property {LH.NavigationRequestor} requestor
@@ -218,6 +219,7 @@ async function _navigation(navigationContext) {
218
219
  url: initialUrl,
219
220
  gatherMode: /** @type {const} */ ('navigation'),
220
221
  driver: navigationContext.driver,
222
+ page: navigationContext.page,
221
223
  computedCache: navigationContext.computedCache,
222
224
  artifactDefinitions: navigationContext.navigation.artifacts,
223
225
  artifactState,
@@ -250,10 +252,10 @@ async function _navigation(navigationContext) {
250
252
  }
251
253
 
252
254
  /**
253
- * @param {{driver: Driver, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']}} args
255
+ * @param {{driver: Driver, page: LH.Puppeteer.Page, config: LH.Config.FRConfig, requestor: LH.NavigationRequestor; baseArtifacts: LH.FRBaseArtifacts, computedCache: NavigationContext['computedCache']}} args
254
256
  * @return {Promise<{artifacts: Partial<LH.FRArtifacts & LH.FRBaseArtifacts>}>}
255
257
  */
256
- async function _navigations({driver, config, requestor, baseArtifacts, computedCache}) {
258
+ async function _navigations({driver, page, config, requestor, baseArtifacts, computedCache}) {
257
259
  if (!config.navigations) throw new Error('No navigations configured');
258
260
 
259
261
  /** @type {Partial<LH.FRArtifacts & LH.FRBaseArtifacts>} */
@@ -264,6 +266,7 @@ async function _navigations({driver, config, requestor, baseArtifacts, computedC
264
266
  for (const navigation of config.navigations) {
265
267
  const navigationContext = {
266
268
  driver,
269
+ page,
267
270
  navigation,
268
271
  requestor,
269
272
  config,
@@ -316,7 +319,7 @@ async function navigationGather(requestor, options) {
316
319
  const artifacts = await Runner.gather(
317
320
  async () => {
318
321
  let {page} = options;
319
- const normalizedRequestor = isCallback ? requestor : URL.normalizeUrl(requestor);
322
+ const normalizedRequestor = isCallback ? requestor : UrlUtils.normalizeUrl(requestor);
320
323
 
321
324
  // For navigation mode, we shouldn't connect to a browser in audit mode,
322
325
  // therefore we connect to the browser in the gatherFn callback.
@@ -333,7 +336,7 @@ async function navigationGather(requestor, options) {
333
336
  requestor: normalizedRequestor,
334
337
  };
335
338
  const {baseArtifacts} = await _setup(context);
336
- const {artifacts} = await _navigations({...context, baseArtifacts, computedCache});
339
+ const {artifacts} = await _navigations({...context, page, baseArtifacts, computedCache});
337
340
  await _cleanup(context);
338
341
 
339
342
  return finalizeArtifacts(baseArtifacts, artifacts);
@@ -7,6 +7,7 @@
7
7
  /**
8
8
  * @typedef CollectPhaseArtifactOptions
9
9
  * @property {import('./driver.js').Driver} driver
10
+ * @property {LH.Puppeteer.Page} page
10
11
  * @property {Array<LH.Config.AnyArtifactDefn>} artifactDefinitions
11
12
  * @property {ArtifactState} artifactState
12
13
  * @property {LH.FRBaseArtifacts} baseArtifacts
@@ -66,6 +67,7 @@ const phaseToPriorPhase = {
66
67
  async function collectPhaseArtifacts(options) {
67
68
  const {
68
69
  driver,
70
+ page,
69
71
  artifactDefinitions,
70
72
  artifactState,
71
73
  baseArtifacts,
@@ -91,6 +93,7 @@ async function collectPhaseArtifacts(options) {
91
93
  return gatherer[phase]({
92
94
  gatherMode,
93
95
  driver,
96
+ page,
94
97
  baseArtifacts,
95
98
  dependencies,
96
99
  computedCache,
@@ -17,11 +17,11 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
17
17
  * @return {Promise<LH.Gatherer.FRGatherResult>}
18
18
  */
19
19
  async function snapshotGather(options) {
20
- const {flags = {}} = options;
20
+ const {page, flags = {}} = options;
21
21
  log.setLevel(flags.logLevel || 'error');
22
22
 
23
23
  const {config} = await initializeConfig('snapshot', options.config, flags);
24
- const driver = new Driver(options.page);
24
+ const driver = new Driver(page);
25
25
  await driver.connect();
26
26
 
27
27
  /** @type {Map<string, LH.ArbitraryEqualityMap>} */
@@ -43,6 +43,7 @@ async function snapshotGather(options) {
43
43
  phase: 'getArtifact',
44
44
  gatherMode: 'snapshot',
45
45
  driver,
46
+ page,
46
47
  baseArtifacts,
47
48
  artifactDefinitions,
48
49
  artifactState,
@@ -18,11 +18,11 @@ import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
18
18
  * @return {Promise<{endTimespanGather(): Promise<LH.Gatherer.FRGatherResult>}>}
19
19
  */
20
20
  async function startTimespanGather(options) {
21
- const {flags = {}} = options;
21
+ const {page, flags = {}} = options;
22
22
  log.setLevel(flags.logLevel || 'error');
23
23
 
24
24
  const {config} = await initializeConfig('timespan', options.config, flags);
25
- const driver = new Driver(options.page);
25
+ const driver = new Driver(page);
26
26
  await driver.connect();
27
27
 
28
28
  /** @type {Map<string, LH.ArbitraryEqualityMap>} */
@@ -34,6 +34,7 @@ async function startTimespanGather(options) {
34
34
  /** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
35
35
  const phaseOptions = {
36
36
  driver,
37
+ page,
37
38
  artifactDefinitions,
38
39
  artifactState,
39
40
  baseArtifacts,
@@ -10,7 +10,7 @@ import {NetworkMonitor} from './network-monitor.js';
10
10
  import {waitForFullyLoaded, waitForFrameNavigated, waitForUserToContinue} from './wait-for-condition.js'; // eslint-disable-line max-len
11
11
  import * as constants from '../../config/constants.js';
12
12
  import * as i18n from '../../lib/i18n/i18n.js';
13
- import URL from '../../lib/url-shim.js';
13
+ import UrlUtils from '../../lib/url-utils.js';
14
14
 
15
15
  const UIStrings = {
16
16
  /**
@@ -130,7 +130,7 @@ async function gotoURL(driver, requestor, options) {
130
130
 
131
131
  let requestedUrl = navigationUrls.requestedUrl;
132
132
  if (typeof requestor === 'string') {
133
- if (requestedUrl && !URL.equalWithExcludedFragments(requestor, requestedUrl)) {
133
+ if (requestedUrl && !UrlUtils.equalWithExcludedFragments(requestor, requestedUrl)) {
134
134
  log.error(
135
135
  'Navigation',
136
136
  `Provided URL (${requestor}) did not match initial navigation URL (${requestedUrl})`
@@ -169,7 +169,7 @@ function getNavigationWarnings(navigation) {
169
169
 
170
170
  if (navigation.timedOut) warnings.push(str_(UIStrings.warningTimeout));
171
171
 
172
- if (!URL.equalWithExcludedFragments(requestedUrl, mainDocumentUrl)) {
172
+ if (!UrlUtils.equalWithExcludedFragments(requestedUrl, mainDocumentUrl)) {
173
173
  warnings.push(str_(UIStrings.warningRedirected, {
174
174
  requested: requestedUrl,
175
175
  final: mainDocumentUrl,
@@ -15,7 +15,7 @@ import log from 'lighthouse-logger';
15
15
 
16
16
  import {NetworkRecorder} from '../../lib/network-recorder.js';
17
17
  import {NetworkRequest} from '../../lib/network-request.js';
18
- import URL from '../../lib/url-shim.js';
18
+ import UrlUtils from '../../lib/url-utils.js';
19
19
 
20
20
  /** @typedef {import('../../lib/network-recorder.js').NetworkRecorderEventMap} NetworkRecorderEventMap */
21
21
  /** @typedef {'network-2-idle'|'network-critical-idle'|'networkidle'|'networkbusy'|'network-critical-busy'|'network-2-busy'} NetworkMonitorEvent_ */
@@ -214,7 +214,7 @@ class NetworkMonitor extends NetworkMonitorEventEmitter {
214
214
  /** @type {Array<{time: number, isStart: boolean}>} */
215
215
  let timeBoundaries = [];
216
216
  requests.forEach(request => {
217
- if (URL.isNonNetworkProtocol(request.protocol)) return;
217
+ if (UrlUtils.isNonNetworkProtocol(request.protocol)) return;
218
218
  if (request.protocol === 'ws' || request.protocol === 'wss') return;
219
219
 
220
220
  // convert the network timestamp to ms
@@ -21,7 +21,7 @@ import InstallabilityErrors from './gatherers/installability-errors.js';
21
21
  import NetworkUserAgent from './gatherers/network-user-agent.js';
22
22
  import Stacks from './gatherers/stacks.js';
23
23
  import {finalizeArtifacts} from '../fraggle-rock/gather/base-artifacts.js';
24
- import URLShim from '../lib/url-shim.js';
24
+ import UrlUtils from '../lib/url-utils.js';
25
25
 
26
26
  /** @typedef {import('../gather/driver.js').Driver} Driver */
27
27
  /** @typedef {import('../lib/arbitrary-equality-map.js').ArbitraryEqualityMap} ArbitraryEqualityMap */
@@ -494,7 +494,7 @@ class GatherRunner {
494
494
 
495
495
  // Hack for running benchmarkIndex extra times.
496
496
  // Add a `bidx=20` query param, eg: https://www.example.com/?bidx=50
497
- const parsedUrl = URLShim.isValid(options.requestedUrl) && new URL(options.requestedUrl);
497
+ const parsedUrl = UrlUtils.isValid(options.requestedUrl) && new URL(options.requestedUrl);
498
498
  if (options.settings.channel === 'lr' && parsedUrl && parsedUrl.searchParams.has('bidx')) {
499
499
  const bidxRunCount = parsedUrl.searchParams.get('bidx') || 0;
500
500
  // Add the first bidx into the new set
@@ -13,7 +13,7 @@
13
13
  import log from 'lighthouse-logger';
14
14
 
15
15
  import FRGatherer from '../../../fraggle-rock/gather/base-gatherer.js';
16
- import URL from '../../../lib/url-shim.js';
16
+ import UrlUtils from '../../../lib/url-utils.js';
17
17
  import {NetworkRequest} from '../../../lib/network-request.js';
18
18
  import {Sentry} from '../../../lib/sentry.js';
19
19
  import NetworkRecords from '../../../computed/network-records.js';
@@ -139,7 +139,7 @@ class OptimizedImages extends FRGatherer {
139
139
  // want to tank the entire run due to a single image.
140
140
  Sentry.captureException(err, {
141
141
  tags: {gatherer: 'OptimizedImages'},
142
- extra: {imageUrl: URL.elideDataURI(record.url)},
142
+ extra: {imageUrl: UrlUtils.elideDataURI(record.url)},
143
143
  level: 'warning',
144
144
  });
145
145
 
@@ -14,7 +14,7 @@ import {Buffer} from 'buffer';
14
14
  import {gzip} from 'zlib';
15
15
 
16
16
  import FRGatherer from '../../../fraggle-rock/gather/base-gatherer.js';
17
- import URL from '../../../lib/url-shim.js';
17
+ import UrlUtils from '../../../lib/url-utils.js';
18
18
  import {Sentry} from '../../../lib/sentry.js';
19
19
  import {NetworkRequest} from '../../../lib/network-request.js';
20
20
  import DevtoolsLog from '../devtools-log.js';
@@ -122,7 +122,7 @@ class ResponseCompression extends FRGatherer {
122
122
  }).catch(err => {
123
123
  Sentry.captureException(err, {
124
124
  tags: {gatherer: 'ResponseCompression'},
125
- extra: {url: URL.elideDataURI(record.url)},
125
+ extra: {url: UrlUtils.elideDataURI(record.url)},
126
126
  level: 'warning',
127
127
  });
128
128
 
@@ -7,7 +7,6 @@
7
7
  import LinkHeader from 'http-link-header';
8
8
 
9
9
  import FRGatherer from '../../fraggle-rock/gather/base-gatherer.js';
10
- import URL from '../../lib/url-shim.js';
11
10
  import {pageFunctions} from '../../lib/page-functions.js';
12
11
  import DevtoolsLog from './devtools-log.js';
13
12
  import MainResource from '../../computed/main-resource.js';
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import FRGatherer from '../../fraggle-rock/gather/base-gatherer.js';
8
- import URL from '../../lib/url-shim.js';
9
8
 
10
9
  /**
11
10
  * @fileoverview Gets JavaScript source maps.
package/core/index.js CHANGED
@@ -9,7 +9,7 @@ import log from 'lighthouse-logger';
9
9
  import {Runner} from './runner.js';
10
10
  import {CriConnection} from './gather/connections/cri.js';
11
11
  import {Config} from './config/config.js';
12
- import URL from './lib/url-shim.js';
12
+ import UrlUtils from './lib/url-utils.js';
13
13
  import * as fraggleRock from './fraggle-rock/api.js';
14
14
  import {Driver} from './gather/driver.js';
15
15
  import {initializeConfig} from './fraggle-rock/config/config.js';
@@ -67,7 +67,7 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
67
67
 
68
68
  // kick off a lighthouse run
69
69
  const artifacts = await Runner.gather(() => {
70
- const requestedUrl = URL.normalizeUrl(url);
70
+ const requestedUrl = UrlUtils.normalizeUrl(url);
71
71
  return Runner._gatherArtifactsFromBrowser(requestedUrl, options, connection);
72
72
  }, options);
73
73
  return Runner.audit(artifacts, options);
@@ -4,7 +4,7 @@
4
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
5
  */
6
6
 
7
- import URL from '../../url-shim.js';
7
+ import UrlUtils from '../../url-utils.js';
8
8
 
9
9
  const INITIAL_CWD = 14 * 1024;
10
10
 
@@ -440,7 +440,7 @@ class NetworkAnalyzer {
440
440
  // equalWithExcludedFragments is expensive, so check that the resourceUrl starts with the request url first
441
441
  return records.find(request =>
442
442
  resourceUrl.startsWith(request.url) &&
443
- URL.equalWithExcludedFragments(request.url, resourceUrl)
443
+ UrlUtils.equalWithExcludedFragments(request.url, resourceUrl)
444
444
  );
445
445
  }
446
446
 
package/core/lib/icons.js CHANGED
@@ -4,8 +4,6 @@
4
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
5
  */
6
6
 
7
- import URL from './url-shim.js';
8
-
9
7
  /**
10
8
  * @param {NonNullable<LH.Artifacts.Manifest['value']>} manifest
11
9
  * @return {boolean} Does the manifest have any icons?
@@ -6,8 +6,6 @@
6
6
 
7
7
  import cssParsers from 'cssstyle/lib/parsers.js';
8
8
 
9
- import URL from './url-shim.js';
10
-
11
9
  const ALLOWED_DISPLAY_VALUES = [
12
10
  'fullscreen',
13
11
  'standalone',
@@ -52,7 +52,7 @@
52
52
  Trace: ResourceFinish.ts
53
53
  */
54
54
 
55
- import URL from './url-shim.js';
55
+ import UrlUtils from './url-utils.js';
56
56
 
57
57
  // Lightrider X-Header names for timing information.
58
58
  // See: _updateTransferSizeForLightrider and _updateTimingsForLightrider.
@@ -215,7 +215,7 @@ class NetworkRequest {
215
215
  host: url.hostname,
216
216
  securityOrigin: url.origin,
217
217
  };
218
- this.isSecure = URL.isSecureScheme(this.parsedURL.scheme);
218
+ this.isSecure = UrlUtils.isSecureScheme(this.parsedURL.scheme);
219
219
 
220
220
  this.startTime = data.timestamp;
221
221
 
@@ -522,9 +522,9 @@ class NetworkRequest {
522
522
  */
523
523
  static isNonNetworkRequest(record) {
524
524
  // The 'protocol' field in devtools a string more like a `scheme`
525
- return URL.isNonNetworkProtocol(record.protocol) ||
525
+ return UrlUtils.isNonNetworkProtocol(record.protocol) ||
526
526
  // But `protocol` can fail to be populated if the request fails, so fallback to scheme.
527
- URL.isNonNetworkProtocol(record.parsedURL.scheme);
527
+ UrlUtils.isNonNetworkProtocol(record.parsedURL.scheme);
528
528
  }
529
529
 
530
530
  /**
@@ -535,9 +535,9 @@ class NetworkRequest {
535
535
  * @return {boolean}
536
536
  */
537
537
  static isSecureRequest(record) {
538
- return URL.isSecureScheme(record.parsedURL.scheme) ||
539
- URL.isSecureScheme(record.protocol) ||
540
- URL.isLikeLocalhost(record.parsedURL.host) ||
538
+ return UrlUtils.isSecureScheme(record.parsedURL.scheme) ||
539
+ UrlUtils.isSecureScheme(record.protocol) ||
540
+ UrlUtils.isLikeLocalhost(record.parsedURL.host) ||
541
541
  NetworkRequest.isHstsRequest(record);
542
542
  }
543
543
 
@@ -4,12 +4,8 @@
4
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
5
  */
6
6
 
7
- /**
8
- * URL shim so we keep our code DRY
9
- */
10
-
11
7
  import {Util} from '../util.cjs';
12
- import {LighthouseError} from '../lib/lh-error.js';
8
+ import {LighthouseError} from './lh-error.js';
13
9
 
14
10
  /** @typedef {import('./network-request.js').NetworkRequest} NetworkRequest */
15
11
 
@@ -43,8 +39,7 @@ function rewriteChromeInternalUrl(url) {
43
39
  return url.replace(/^chrome:\/\/chrome\//, 'chrome://');
44
40
  }
45
41
 
46
- // URL is global as of node 10. https://nodejs.org/api/globals.html#globals_url
47
- class URLShim extends URL {
42
+ class UrlUtils {
48
43
  /**
49
44
  * @param {string} url
50
45
  * @return {boolean}
@@ -264,11 +259,8 @@ class URLShim extends URL {
264
259
  }
265
260
  }
266
261
 
267
- URLShim.URL = URL;
268
-
269
- URLShim.INVALID_URL_DEBUG_STRING =
262
+ UrlUtils.INVALID_URL_DEBUG_STRING =
270
263
  'Lighthouse was unable to determine the URL of some script executions. ' +
271
264
  'It\'s possible a Chrome extension or other eval\'d code is the source.';
272
265
 
273
- // TODO(esmodules): don't use default export
274
- export default URLShim;
266
+ export default UrlUtils;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "9.5.0-dev.20220904",
4
+ "version": "9.5.0-dev.20220907",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -98,7 +98,6 @@
98
98
  "@build-tracker/cli": "^1.0.0-beta.15",
99
99
  "@esbuild-kit/esm-loader": "^2.1.1",
100
100
  "@jest/fake-timers": "^28.1.0",
101
- "@puppeteer/replay": "^0.6.1",
102
101
  "@rollup/plugin-alias": "^3.1.2",
103
102
  "@rollup/plugin-commonjs": "^20.0.0",
104
103
  "@rollup/plugin-dynamic-import-vars": "^1.1.1",
@@ -10,7 +10,6 @@ import jsdom from 'jsdom';
10
10
 
11
11
  import {Util} from '../../renderer/util.js';
12
12
  import {I18n} from '../../renderer/i18n.js';
13
- import URL from '../../../core/lib/url-shim.js';
14
13
  import {DOM} from '../../renderer/dom.js';
15
14
  import {DetailsRenderer} from '../../renderer/details-renderer.js';
16
15
  import {PerformanceCategoryRenderer} from '../../renderer/performance-category-renderer.js';
@@ -10,7 +10,6 @@ import jsdom from 'jsdom';
10
10
  import jestMock from 'jest-mock';
11
11
 
12
12
  import {Util} from '../../renderer/util.js';
13
- import URL from '../../../core/lib/url-shim.js';
14
13
  import {DOM} from '../../renderer/dom.js';
15
14
  import {DetailsRenderer} from '../../renderer/details-renderer.js';
16
15
  import {CategoryRenderer} from '../../renderer/category-renderer.js';
package/tsconfig.json CHANGED
@@ -99,7 +99,7 @@
99
99
  "core/test/lib/tracehouse/task-summary-test.js",
100
100
  "core/test/lib/tracehouse/trace-processor-test.js",
101
101
  "core/test/lib/traces/metrics-trace-events-test.js",
102
- "core/test/lib/url-shim-test.js",
102
+ "core/test/lib/url-utils-test.js",
103
103
  "core/test/network-records-to-devtools-log-test.js",
104
104
  "core/test/runner-test.js",
105
105
  "core/test/scoring-test.js",
@@ -52,6 +52,8 @@ declare module Gatherer {
52
52
  gatherMode: GatherMode;
53
53
  /** The connection to the page being analyzed. */
54
54
  driver: FRTransitionalDriver;
55
+ /** The Puppeteer page handle. Will be undefined in legacy navigation mode. */
56
+ page?: LH.Puppeteer.Page;
55
57
  /** The set of base artifacts that are always collected. */
56
58
  baseArtifacts: FRBaseArtifacts;
57
59
  /** The cached results of computed artifacts. */
@@ -1,104 +0,0 @@
1
- /**
2
- * @license Copyright 2022 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
-
7
- import * as PuppeteerReplay from '@puppeteer/replay';
8
-
9
- /**
10
- * @param {PuppeteerReplay.Schema.Step} step
11
- * @return {boolean}
12
- */
13
- function isNavigationStep(step) {
14
- return Boolean(
15
- step.type === 'navigate' ||
16
- step.assertedEvents?.some(event => event.type === 'navigation')
17
- );
18
- }
19
-
20
- class LighthouseStringifyExtension extends PuppeteerReplay.PuppeteerStringifyExtension {
21
- #isProcessingTimespan = false;
22
-
23
- /**
24
- * @override
25
- * @param {PuppeteerReplay.LineWriter} out
26
- * @param {PuppeteerReplay.Schema.UserFlow} flow
27
- */
28
- async beforeAllSteps(out, flow) {
29
- out.appendLine(`const fs = require('fs');`);
30
-
31
- let isMobile = true;
32
- for (const step of flow.steps) {
33
- if (step.type !== 'setViewport') continue;
34
- isMobile = step.isMobile;
35
- }
36
-
37
- await super.beforeAllSteps(out, flow);
38
-
39
- const flags = {
40
- screenEmulation: {
41
- disabled: true,
42
- },
43
- };
44
- out.appendLine(`const flags = ${JSON.stringify(flags)}`);
45
- if (isMobile) {
46
- out.appendLine(`const config = undefined;`);
47
- } else {
48
- // eslint-disable-next-line max-len
49
- out.appendLine(`const config = (await import('lighthouse/core/config/desktop-config.js')).default;`);
50
- }
51
-
52
- out.appendLine(`const lhApi = await import('lighthouse/core/fraggle-rock/api.js');`);
53
- // eslint-disable-next-line max-len
54
- out.appendLine(`const lhFlow = await lhApi.startFlow(page, {name: ${JSON.stringify(flow.title)}, config, flags});`);
55
- }
56
-
57
- /**
58
- * @override
59
- * @param {PuppeteerReplay.LineWriter} out
60
- * @param {PuppeteerReplay.Schema.Step} step
61
- * @param {PuppeteerReplay.Schema.UserFlow} flow
62
- */
63
- async stringifyStep(out, step, flow) {
64
- if (step.type === 'setViewport') {
65
- await super.stringifyStep(out, step, flow);
66
- return;
67
- }
68
-
69
- const isNavigation = isNavigationStep(step);
70
-
71
- if (isNavigation) {
72
- if (this.#isProcessingTimespan) {
73
- out.appendLine(`await lhFlow.endTimespan();`);
74
- this.#isProcessingTimespan = false;
75
- }
76
- out.appendLine(`await lhFlow.startNavigation();`);
77
- } else if (!this.#isProcessingTimespan) {
78
- out.appendLine(`await lhFlow.startTimespan();`);
79
- this.#isProcessingTimespan = true;
80
- }
81
-
82
- await super.stringifyStep(out, step, flow);
83
-
84
- if (isNavigation) {
85
- out.appendLine(`await lhFlow.endNavigation();`);
86
- }
87
- }
88
-
89
- /**
90
- * @override
91
- * @param {PuppeteerReplay.LineWriter} out
92
- * @param {PuppeteerReplay.Schema.UserFlow} flow
93
- */
94
- async afterAllSteps(out, flow) {
95
- if (this.#isProcessingTimespan) {
96
- out.appendLine(`await lhFlow.endTimespan();`);
97
- }
98
- out.appendLine(`const lhFlowReport = await lhFlow.generateReport();`);
99
- out.appendLine(`fs.writeFileSync(__dirname + '/flow.report.html', lhFlowReport)`);
100
- await super.afterAllSteps(out, flow);
101
- }
102
- }
103
-
104
- export default LighthouseStringifyExtension;