lighthouse 10.2.0-dev.20230515 → 10.2.0-dev.20230517

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.
@@ -43,6 +43,7 @@ class CrawlableAnchors extends Audit {
43
43
  rawHref,
44
44
  name = '',
45
45
  role = '',
46
+ id,
46
47
  }) => {
47
48
  rawHref = rawHref.replace( /\s/g, '');
48
49
  name = name.trim();
@@ -52,6 +53,9 @@ class CrawlableAnchors extends Audit {
52
53
  // Ignore mailto links even if they use one of the failing patterns. See https://github.com/GoogleChrome/lighthouse/issues/11443#issuecomment-694898412
53
54
  if (rawHref.startsWith('mailto:')) return;
54
55
 
56
+ // Ignore `<a id="something">` elements acting as an anchor.
57
+ if (rawHref === '' && id) return;
58
+
55
59
  const javaScriptVoidRegExp = /javascript:void(\(|)0(\)|)/;
56
60
 
57
61
  if (rawHref.startsWith('file:')) return true;
@@ -10,12 +10,25 @@ declare const EntityClassificationComputed: typeof EntityClassification & {
10
10
  };
11
11
  /** @typedef {Map<string, LH.Artifacts.Entity>} EntityCache */
12
12
  declare class EntityClassification {
13
+ /**
14
+ * @param {EntityCache} entityCache
15
+ * @param {string} url
16
+ * @param {string=} extensionName
17
+ * @return {LH.Artifacts.Entity}
18
+ */
19
+ static makeupChromeExtensionEntity_(entityCache: EntityCache, url: string, extensionName?: string | undefined): LH.Artifacts.Entity;
13
20
  /**
14
21
  * @param {EntityCache} entityCache
15
22
  * @param {string} url
16
23
  * @return {LH.Artifacts.Entity | undefined}
17
24
  */
18
- static makeUpAnEntity(entityCache: EntityCache, url: string): LH.Artifacts.Entity | undefined;
25
+ static _makeUpAnEntity(entityCache: EntityCache, url: string): LH.Artifacts.Entity | undefined;
26
+ /**
27
+ * Preload Chrome extensions found in the devtoolsLog into cache.
28
+ * @param {EntityCache} entityCache
29
+ * @param {LH.DevtoolsLog} devtoolsLog
30
+ */
31
+ static _preloadChromeExtensionsToCache(entityCache: EntityCache, devtoolsLog: import("../index.js").DevtoolsLog): void;
19
32
  /**
20
33
  * @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog}} data
21
34
  * @param {LH.Artifacts.ComputedContext} context
@@ -13,16 +13,51 @@ import thirdPartyWeb from '../lib/third-party-web.js';
13
13
  /** @typedef {Map<string, LH.Artifacts.Entity>} EntityCache */
14
14
 
15
15
  class EntityClassification {
16
+ /**
17
+ * @param {EntityCache} entityCache
18
+ * @param {string} url
19
+ * @param {string=} extensionName
20
+ * @return {LH.Artifacts.Entity}
21
+ */
22
+ static makeupChromeExtensionEntity_(entityCache, url, extensionName) {
23
+ const origin = Util.getChromeExtensionOrigin(url);
24
+ const host = new URL(origin).host;
25
+ const name = extensionName || host;
26
+
27
+ const cachedEntity = entityCache.get(origin);
28
+ if (cachedEntity) return cachedEntity;
29
+
30
+ const chromeExtensionEntity = {
31
+ name,
32
+ company: name,
33
+ category: 'Chrome Extension',
34
+ homepage: 'https://chromewebstore.google.com/detail/' + host,
35
+ categories: [],
36
+ domains: [],
37
+ averageExecutionTime: 0,
38
+ totalExecutionTime: 0,
39
+ totalOccurrences: 0,
40
+ };
41
+
42
+ entityCache.set(origin, chromeExtensionEntity);
43
+ return chromeExtensionEntity;
44
+ }
45
+
16
46
  /**
17
47
  * @param {EntityCache} entityCache
18
48
  * @param {string} url
19
49
  * @return {LH.Artifacts.Entity | undefined}
20
50
  */
21
- static makeUpAnEntity(entityCache, url) {
51
+ static _makeUpAnEntity(entityCache, url) {
22
52
  if (!UrlUtils.isValid(url)) return;
23
- // We can make up an entity only for those URLs with a valid domain attached.
24
- // So we further restrict from allowed URLs to (http/https).
25
- if (!Util.createOrReturnURL(url).protocol.startsWith('http')) return;
53
+
54
+ const parsedUrl = Util.createOrReturnURL(url);
55
+ if (parsedUrl.protocol === 'chrome-extension:') {
56
+ return EntityClassification.makeupChromeExtensionEntity_(entityCache, url);
57
+ }
58
+
59
+ // Make up an entity only for valid http/https URLs.
60
+ if (!parsedUrl.protocol.startsWith('http')) return;
26
61
 
27
62
  const rootDomain = Util.getRootDomain(url);
28
63
  if (!rootDomain) return;
@@ -43,6 +78,24 @@ class EntityClassification {
43
78
  return unrecognizedEntity;
44
79
  }
45
80
 
81
+ /**
82
+ * Preload Chrome extensions found in the devtoolsLog into cache.
83
+ * @param {EntityCache} entityCache
84
+ * @param {LH.DevtoolsLog} devtoolsLog
85
+ */
86
+ static _preloadChromeExtensionsToCache(entityCache, devtoolsLog) {
87
+ for (const entry of devtoolsLog.values()) {
88
+ if (entry.method !== 'Runtime.executionContextCreated') continue;
89
+
90
+ const origin = entry.params.context.origin;
91
+ if (!origin.startsWith('chrome-extension:')) continue;
92
+ if (entityCache.has(origin)) continue;
93
+
94
+ EntityClassification.makeupChromeExtensionEntity_(entityCache, origin,
95
+ entry.params.context.name);
96
+ }
97
+ }
98
+
46
99
  /**
47
100
  * @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog}} data
48
101
  * @param {LH.Artifacts.ComputedContext} context
@@ -57,12 +110,14 @@ class EntityClassification {
57
110
  /** @type {Map<LH.Artifacts.Entity, Set<string>>} */
58
111
  const urlsByEntity = new Map();
59
112
 
113
+ EntityClassification._preloadChromeExtensionsToCache(madeUpEntityCache, data.devtoolsLog);
114
+
60
115
  for (const record of networkRecords) {
61
116
  const {url} = record;
62
117
  if (entityByUrl.has(url)) continue;
63
118
 
64
119
  const entity = thirdPartyWeb.getEntity(url) ||
65
- EntityClassification.makeUpAnEntity(madeUpEntityCache, url);
120
+ EntityClassification._makeUpAnEntity(madeUpEntityCache, url);
66
121
  if (!entity) continue;
67
122
 
68
123
  const entityURLs = urlsByEntity.get(entity) || new Set();
@@ -76,7 +131,7 @@ class EntityClassification {
76
131
  // See https://github.com/GoogleChrome/lighthouse/issues/13706
77
132
  const firstPartyUrl = data.URL.mainDocumentUrl || data.URL.finalDisplayedUrl;
78
133
  const firstParty = thirdPartyWeb.getEntity(firstPartyUrl) ||
79
- EntityClassification.makeUpAnEntity(madeUpEntityCache, firstPartyUrl);
134
+ EntityClassification._makeUpAnEntity(madeUpEntityCache, firstPartyUrl);
80
135
 
81
136
  /**
82
137
  * Convenience function to check if a URL belongs to first party.
@@ -53,6 +53,7 @@ function collectAnchorElements() {
53
53
  text: node.innerText, // we don't want to return hidden text, so use innerText
54
54
  rel: node.rel,
55
55
  target: node.target,
56
+ id: node.getAttribute('id') || '',
56
57
  // @ts-expect-error - getNodeDetails put into scope via stringification
57
58
  node: getNodeDetails(node),
58
59
  };
@@ -66,6 +67,7 @@ function collectAnchorElements() {
66
67
  text: node.textContent || '',
67
68
  rel: '',
68
69
  target: node.target.baseVal || '',
70
+ id: node.getAttribute('id') || '',
69
71
  // @ts-expect-error - getNodeDetails put into scope via stringification
70
72
  node: getNodeDetails(node),
71
73
  };
@@ -87,6 +87,10 @@ class UrlUtils {
87
87
  static getOrigin(url) {
88
88
  try {
89
89
  const urlInfo = new URL(url);
90
+ if (urlInfo.protocol === 'chrome-extension:') {
91
+ // Chrome extensions return string "null" as origin, so we reconstruct the extension origin.
92
+ return Util.getChromeExtensionOrigin(url);
93
+ }
90
94
  // check for both host and origin since some URLs schemes like data and file set origin to the
91
95
  // string "null" instead of the object
92
96
  return (urlInfo.host && urlInfo.origin) || null;
@@ -260,6 +260,16 @@ class Util {
260
260
  return name;
261
261
  }
262
262
 
263
+ /**
264
+ * Returns the origin portion of a Chrome extension URL.
265
+ * @param {string} url
266
+ * @return {string}
267
+ */
268
+ static getChromeExtensionOrigin(url) {
269
+ const parsedUrl = new URL(url);
270
+ return parsedUrl.protocol + '//' + parsedUrl.host;
271
+ }
272
+
263
273
  /**
264
274
  * Split a URL into a file, hostname and origin for easy display.
265
275
  * @param {string} url
@@ -270,7 +280,10 @@ class Util {
270
280
  return {
271
281
  file: Util.getURLDisplayName(parsedUrl),
272
282
  hostname: parsedUrl.hostname,
273
- origin: parsedUrl.origin,
283
+ // Node's URL parsing behavior is different than Chrome and returns 'null'
284
+ // for chrome-extension:// URLs. See https://github.com/nodejs/node/issues/21955.
285
+ origin: parsedUrl.protocol === 'chrome-extension:' ?
286
+ Util.getChromeExtensionOrigin(url) : parsedUrl.origin,
274
287
  };
275
288
  }
276
289
 
@@ -14,7 +14,7 @@
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */const oe="…",re={PASS:{label:"pass",minScore:.9},AVERAGE:{label:"average",minScore:.5},FAIL:{label:"fail"},ERROR:{label:"error"}},se=["com","co","gov","edu","ac","org","go","gob","or","net","in","ne","nic","gouv","web","spb","blog","jus","kiev","mil","wi","qc","ca","bel","on"];class le{static get RATINGS(){return re}static get PASS_THRESHOLD(){return.9}static get MS_DISPLAY_VALUE(){return"%10d ms"}static getFinalDisplayedUrl(e){if(e.finalDisplayedUrl)return e.finalDisplayedUrl;if(e.finalUrl)return e.finalUrl;throw new Error("Could not determine final displayed URL")}static getMainDocumentUrl(e){return e.mainDocumentUrl||e.finalUrl}static getFullPageScreenshot(e){if(e.fullPageScreenshot)return e.fullPageScreenshot;return e.audits["full-page-screenshot"]?.details}static splitMarkdownCodeSpans(e){const a=[],n=e.split(/`(.*?)`/g);for(let e=0;e<n.length;e++){const t=n[e];if(!t)continue;const i=e%2!=0;a.push({isCode:i,text:t})}return a}static splitMarkdownLink(e){const a=[],n=e.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);for(;n.length;){const[e,t,i]=n.splice(0,3);e&&a.push({isLink:!1,text:e}),t&&i&&a.push({isLink:!0,text:t,linkHref:i})}return a}static truncate(e,a,n="…"){if(e.length<=a)return e;const t=new Intl.Segmenter(void 0,{granularity:"grapheme"}).segment(e)[Symbol.iterator]();let i=0;for(let o=0;o<=a-n.length;o++){const a=t.next();if(a.done)return e;i=a.value.index}for(let a=0;a<n.length;a++)if(t.next().done)return e;return e.slice(0,i)+n}static getURLDisplayName(e,a){const n=void 0!==(a=a||{numPathParts:void 0,preserveQuery:void 0,preserveHost:void 0}).numPathParts?a.numPathParts:2,t=void 0===a.preserveQuery||a.preserveQuery,i=a.preserveHost||!1;let o;if("about:"===e.protocol||"data:"===e.protocol)o=e.href;else{o=e.pathname;const a=o.split("/").filter((e=>e.length));n&&a.length>n&&(o=oe+a.slice(-1*n).join("/")),i&&(o=`${e.host}/${o.replace(/^\//,"")}`),t&&(o=`${o}${e.search}`)}if("data:"!==e.protocol&&(o=o.slice(0,200),o=o.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g,"$1…"),o=o.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,"$1…"),o=o.replace(/(\d{3})\d{6,}/g,"$1…"),o=o.replace(/\u2026+/g,oe),o.length>64&&o.includes("?")&&(o=o.replace(/\?([^=]*)(=)?.*/,"?$1$2…"),o.length>64&&(o=o.replace(/\?.*/,"?…")))),o.length>64){const e=o.lastIndexOf(".");o=e>=0?o.slice(0,63-(o.length-e))+`…${o.slice(e)}`:o.slice(0,63)+oe}return o}static parseURL(e){const a=new URL(e);return{file:le.getURLDisplayName(a),hostname:a.hostname,origin:a.origin}}static createOrReturnURL(e){return e instanceof URL?e:new URL(e)}static getTld(e){const a=e.split(".").slice(-2);return se.includes(a[0])?`.${a.join(".")}`:`.${a[a.length-1]}`}static getRootDomain(e){const a=le.createOrReturnURL(e).hostname,n=le.getTld(a).split(".");return a.split(".").slice(-n.length).join(".")}static filterRelevantLines(e,a,n){if(0===a.length)return e.slice(0,2*n+1);const t=new Set;return(a=a.sort(((e,a)=>(e.lineNumber||0)-(a.lineNumber||0)))).forEach((({lineNumber:e})=>{let a=e-n,i=e+n;for(;a<1;)a++,i++;t.has(a-3-1)&&(a-=3);for(let e=a;e<=i;e++){const a=e;t.add(a)}})),e.filter((e=>t.has(e.lineNumber)))}}
17
+ */const oe="…",re={PASS:{label:"pass",minScore:.9},AVERAGE:{label:"average",minScore:.5},FAIL:{label:"fail"},ERROR:{label:"error"}},se=["com","co","gov","edu","ac","org","go","gob","or","net","in","ne","nic","gouv","web","spb","blog","jus","kiev","mil","wi","qc","ca","bel","on"];class le{static get RATINGS(){return re}static get PASS_THRESHOLD(){return.9}static get MS_DISPLAY_VALUE(){return"%10d ms"}static getFinalDisplayedUrl(e){if(e.finalDisplayedUrl)return e.finalDisplayedUrl;if(e.finalUrl)return e.finalUrl;throw new Error("Could not determine final displayed URL")}static getMainDocumentUrl(e){return e.mainDocumentUrl||e.finalUrl}static getFullPageScreenshot(e){if(e.fullPageScreenshot)return e.fullPageScreenshot;return e.audits["full-page-screenshot"]?.details}static splitMarkdownCodeSpans(e){const a=[],n=e.split(/`(.*?)`/g);for(let e=0;e<n.length;e++){const t=n[e];if(!t)continue;const i=e%2!=0;a.push({isCode:i,text:t})}return a}static splitMarkdownLink(e){const a=[],n=e.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);for(;n.length;){const[e,t,i]=n.splice(0,3);e&&a.push({isLink:!1,text:e}),t&&i&&a.push({isLink:!0,text:t,linkHref:i})}return a}static truncate(e,a,n="…"){if(e.length<=a)return e;const t=new Intl.Segmenter(void 0,{granularity:"grapheme"}).segment(e)[Symbol.iterator]();let i=0;for(let o=0;o<=a-n.length;o++){const a=t.next();if(a.done)return e;i=a.value.index}for(let a=0;a<n.length;a++)if(t.next().done)return e;return e.slice(0,i)+n}static getURLDisplayName(e,a){const n=void 0!==(a=a||{numPathParts:void 0,preserveQuery:void 0,preserveHost:void 0}).numPathParts?a.numPathParts:2,t=void 0===a.preserveQuery||a.preserveQuery,i=a.preserveHost||!1;let o;if("about:"===e.protocol||"data:"===e.protocol)o=e.href;else{o=e.pathname;const a=o.split("/").filter((e=>e.length));n&&a.length>n&&(o=oe+a.slice(-1*n).join("/")),i&&(o=`${e.host}/${o.replace(/^\//,"")}`),t&&(o=`${o}${e.search}`)}if("data:"!==e.protocol&&(o=o.slice(0,200),o=o.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g,"$1…"),o=o.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,"$1…"),o=o.replace(/(\d{3})\d{6,}/g,"$1…"),o=o.replace(/\u2026+/g,oe),o.length>64&&o.includes("?")&&(o=o.replace(/\?([^=]*)(=)?.*/,"?$1$2…"),o.length>64&&(o=o.replace(/\?.*/,"?…")))),o.length>64){const e=o.lastIndexOf(".");o=e>=0?o.slice(0,63-(o.length-e))+`…${o.slice(e)}`:o.slice(0,63)+oe}return o}static getChromeExtensionOrigin(e){const a=new URL(e);return a.protocol+"//"+a.host}static parseURL(e){const a=new URL(e);return{file:le.getURLDisplayName(a),hostname:a.hostname,origin:"chrome-extension:"===a.protocol?le.getChromeExtensionOrigin(e):a.origin}}static createOrReturnURL(e){return e instanceof URL?e:new URL(e)}static getTld(e){const a=e.split(".").slice(-2);return se.includes(a[0])?`.${a.join(".")}`:`.${a[a.length-1]}`}static getRootDomain(e){const a=le.createOrReturnURL(e).hostname,n=le.getTld(a).split(".");return a.split(".").slice(-n.length).join(".")}static filterRelevantLines(e,a,n){if(0===a.length)return e.slice(0,2*n+1);const t=new Set;return(a=a.sort(((e,a)=>(e.lineNumber||0)-(a.lineNumber||0)))).forEach((({lineNumber:e})=>{let a=e-n,i=e+n;for(;a<1;)a++,i++;t.has(a-3-1)&&(a-=3);for(let e=a;e<=i;e++){const a=e;t.add(a)}})),e.filter((e=>t.has(e.lineNumber)))}}
18
18
  /**
19
19
  * @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
20
20
  * 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
@@ -14,7 +14,7 @@
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */const e="…",t={PASS:{label:"pass",minScore:.9},AVERAGE:{label:"average",minScore:.5},FAIL:{label:"fail"},ERROR:{label:"error"}},n=["com","co","gov","edu","ac","org","go","gob","or","net","in","ne","nic","gouv","web","spb","blog","jus","kiev","mil","wi","qc","ca","bel","on"];class r{static get RATINGS(){return t}static get PASS_THRESHOLD(){return.9}static get MS_DISPLAY_VALUE(){return"%10d ms"}static getFinalDisplayedUrl(e){if(e.finalDisplayedUrl)return e.finalDisplayedUrl;if(e.finalUrl)return e.finalUrl;throw new Error("Could not determine final displayed URL")}static getMainDocumentUrl(e){return e.mainDocumentUrl||e.finalUrl}static getFullPageScreenshot(e){if(e.fullPageScreenshot)return e.fullPageScreenshot;return e.audits["full-page-screenshot"]?.details}static splitMarkdownCodeSpans(e){const t=[],n=e.split(/`(.*?)`/g);for(let e=0;e<n.length;e++){const r=n[e];if(!r)continue;const o=e%2!=0;t.push({isCode:o,text:r})}return t}static splitMarkdownLink(e){const t=[],n=e.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);for(;n.length;){const[e,r,o]=n.splice(0,3);e&&t.push({isLink:!1,text:e}),r&&o&&t.push({isLink:!0,text:r,linkHref:o})}return t}static truncate(e,t,n="…"){if(e.length<=t)return e;const r=new Intl.Segmenter(void 0,{granularity:"grapheme"}).segment(e)[Symbol.iterator]();let o=0;for(let i=0;i<=t-n.length;i++){const t=r.next();if(t.done)return e;o=t.value.index}for(let t=0;t<n.length;t++)if(r.next().done)return e;return e.slice(0,o)+n}static getURLDisplayName(t,n){const r=void 0!==(n=n||{numPathParts:void 0,preserveQuery:void 0,preserveHost:void 0}).numPathParts?n.numPathParts:2,o=void 0===n.preserveQuery||n.preserveQuery,i=n.preserveHost||!1;let a;if("about:"===t.protocol||"data:"===t.protocol)a=t.href;else{a=t.pathname;const n=a.split("/").filter((e=>e.length));r&&n.length>r&&(a=e+n.slice(-1*r).join("/")),i&&(a=`${t.host}/${a.replace(/^\//,"")}`),o&&(a=`${a}${t.search}`)}if("data:"!==t.protocol&&(a=a.slice(0,200),a=a.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g,"$1…"),a=a.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,"$1…"),a=a.replace(/(\d{3})\d{6,}/g,"$1…"),a=a.replace(/\u2026+/g,e),a.length>64&&a.includes("?")&&(a=a.replace(/\?([^=]*)(=)?.*/,"?$1$2…"),a.length>64&&(a=a.replace(/\?.*/,"?…")))),a.length>64){const t=a.lastIndexOf(".");a=t>=0?a.slice(0,63-(a.length-t))+`…${a.slice(t)}`:a.slice(0,63)+e}return a}static parseURL(e){const t=new URL(e);return{file:r.getURLDisplayName(t),hostname:t.hostname,origin:t.origin}}static createOrReturnURL(e){return e instanceof URL?e:new URL(e)}static getTld(e){const t=e.split(".").slice(-2);return n.includes(t[0])?`.${t.join(".")}`:`.${t[t.length-1]}`}static getRootDomain(e){const t=r.createOrReturnURL(e).hostname,n=r.getTld(t).split(".");return t.split(".").slice(-n.length).join(".")}static filterRelevantLines(e,t,n){if(0===t.length)return e.slice(0,2*n+1);const r=new Set;return(t=t.sort(((e,t)=>(e.lineNumber||0)-(t.lineNumber||0)))).forEach((({lineNumber:e})=>{let t=e-n,o=e+n;for(;t<1;)t++,o++;r.has(t-3-1)&&(t-=3);for(let e=t;e<=o;e++){const t=e;r.add(t)}})),e.filter((e=>r.has(e.lineNumber)))}}
17
+ */const e="…",t={PASS:{label:"pass",minScore:.9},AVERAGE:{label:"average",minScore:.5},FAIL:{label:"fail"},ERROR:{label:"error"}},n=["com","co","gov","edu","ac","org","go","gob","or","net","in","ne","nic","gouv","web","spb","blog","jus","kiev","mil","wi","qc","ca","bel","on"];class r{static get RATINGS(){return t}static get PASS_THRESHOLD(){return.9}static get MS_DISPLAY_VALUE(){return"%10d ms"}static getFinalDisplayedUrl(e){if(e.finalDisplayedUrl)return e.finalDisplayedUrl;if(e.finalUrl)return e.finalUrl;throw new Error("Could not determine final displayed URL")}static getMainDocumentUrl(e){return e.mainDocumentUrl||e.finalUrl}static getFullPageScreenshot(e){if(e.fullPageScreenshot)return e.fullPageScreenshot;return e.audits["full-page-screenshot"]?.details}static splitMarkdownCodeSpans(e){const t=[],n=e.split(/`(.*?)`/g);for(let e=0;e<n.length;e++){const r=n[e];if(!r)continue;const o=e%2!=0;t.push({isCode:o,text:r})}return t}static splitMarkdownLink(e){const t=[],n=e.split(/\[([^\]]+?)\]\((https?:\/\/.*?)\)/g);for(;n.length;){const[e,r,o]=n.splice(0,3);e&&t.push({isLink:!1,text:e}),r&&o&&t.push({isLink:!0,text:r,linkHref:o})}return t}static truncate(e,t,n="…"){if(e.length<=t)return e;const r=new Intl.Segmenter(void 0,{granularity:"grapheme"}).segment(e)[Symbol.iterator]();let o=0;for(let i=0;i<=t-n.length;i++){const t=r.next();if(t.done)return e;o=t.value.index}for(let t=0;t<n.length;t++)if(r.next().done)return e;return e.slice(0,o)+n}static getURLDisplayName(t,n){const r=void 0!==(n=n||{numPathParts:void 0,preserveQuery:void 0,preserveHost:void 0}).numPathParts?n.numPathParts:2,o=void 0===n.preserveQuery||n.preserveQuery,i=n.preserveHost||!1;let a;if("about:"===t.protocol||"data:"===t.protocol)a=t.href;else{a=t.pathname;const n=a.split("/").filter((e=>e.length));r&&n.length>r&&(a=e+n.slice(-1*r).join("/")),i&&(a=`${t.host}/${a.replace(/^\//,"")}`),o&&(a=`${a}${t.search}`)}if("data:"!==t.protocol&&(a=a.slice(0,200),a=a.replace(/([a-f0-9]{7})[a-f0-9]{13}[a-f0-9]*/g,"$1…"),a=a.replace(/([a-zA-Z0-9-_]{9})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9-_]{10,}/g,"$1…"),a=a.replace(/(\d{3})\d{6,}/g,"$1…"),a=a.replace(/\u2026+/g,e),a.length>64&&a.includes("?")&&(a=a.replace(/\?([^=]*)(=)?.*/,"?$1$2…"),a.length>64&&(a=a.replace(/\?.*/,"?…")))),a.length>64){const t=a.lastIndexOf(".");a=t>=0?a.slice(0,63-(a.length-t))+`…${a.slice(t)}`:a.slice(0,63)+e}return a}static getChromeExtensionOrigin(e){const t=new URL(e);return t.protocol+"//"+t.host}static parseURL(e){const t=new URL(e);return{file:r.getURLDisplayName(t),hostname:t.hostname,origin:"chrome-extension:"===t.protocol?r.getChromeExtensionOrigin(e):t.origin}}static createOrReturnURL(e){return e instanceof URL?e:new URL(e)}static getTld(e){const t=e.split(".").slice(-2);return n.includes(t[0])?`.${t.join(".")}`:`.${t[t.length-1]}`}static getRootDomain(e){const t=r.createOrReturnURL(e).hostname,n=r.getTld(t).split(".");return t.split(".").slice(-n.length).join(".")}static filterRelevantLines(e,t,n){if(0===t.length)return e.slice(0,2*n+1);const r=new Set;return(t=t.sort(((e,t)=>(e.lineNumber||0)-(t.lineNumber||0)))).forEach((({lineNumber:e})=>{let t=e-n,o=e+n;for(;t<1;)t++,o++;r.has(t-3-1)&&(t-=3);for(let e=t;e<=o;e++){const t=e;r.add(t)}})),e.filter((e=>r.has(e.lineNumber)))}}
18
18
  /**
19
19
  * @license
20
20
  * Copyright 2017 The Lighthouse Authors. All Rights Reserved.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "10.2.0-dev.20230515",
4
+ "version": "10.2.0-dev.20230517",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -187,7 +187,7 @@
187
187
  },
188
188
  "dependencies": {
189
189
  "@sentry/node": "^6.17.4",
190
- "axe-core": "4.7.0",
190
+ "axe-core": "4.7.1",
191
191
  "chrome-launcher": "^0.15.2",
192
192
  "configstore": "^5.0.1",
193
193
  "csp_evaluator": "1.1.1",
@@ -197,7 +197,7 @@
197
197
  "intl-messageformat": "^4.4.0",
198
198
  "jpeg-js": "^0.4.4",
199
199
  "js-library-detector": "^6.6.0",
200
- "lighthouse-logger": "^1.3.0",
200
+ "lighthouse-logger": "^1.4.1",
201
201
  "lighthouse-stack-packs": "1.10.0",
202
202
  "lodash": "^4.17.21",
203
203
  "lookup-closest-locale": "6.2.0",
package/shared/util.d.ts CHANGED
@@ -83,6 +83,12 @@ export class Util {
83
83
  preserveQuery?: boolean | undefined;
84
84
  preserveHost?: boolean | undefined;
85
85
  } | undefined): string;
86
+ /**
87
+ * Returns the origin portion of a Chrome extension URL.
88
+ * @param {string} url
89
+ * @return {string}
90
+ */
91
+ static getChromeExtensionOrigin(url: string): string;
86
92
  /**
87
93
  * Split a URL into a file, hostname and origin for easy display.
88
94
  * @param {string} url
package/shared/util.js CHANGED
@@ -260,6 +260,16 @@ class Util {
260
260
  return name;
261
261
  }
262
262
 
263
+ /**
264
+ * Returns the origin portion of a Chrome extension URL.
265
+ * @param {string} url
266
+ * @return {string}
267
+ */
268
+ static getChromeExtensionOrigin(url) {
269
+ const parsedUrl = new URL(url);
270
+ return parsedUrl.protocol + '//' + parsedUrl.host;
271
+ }
272
+
263
273
  /**
264
274
  * Split a URL into a file, hostname and origin for easy display.
265
275
  * @param {string} url
@@ -270,7 +280,10 @@ class Util {
270
280
  return {
271
281
  file: Util.getURLDisplayName(parsedUrl),
272
282
  hostname: parsedUrl.hostname,
273
- origin: parsedUrl.origin,
283
+ // Node's URL parsing behavior is different than Chrome and returns 'null'
284
+ // for chrome-extension:// URLs. See https://github.com/nodejs/node/issues/21955.
285
+ origin: parsedUrl.protocol === 'chrome-extension:' ?
286
+ Util.getChromeExtensionOrigin(url) : parsedUrl.origin,
274
287
  };
275
288
  }
276
289
 
@@ -410,6 +410,7 @@ declare module Artifacts {
410
410
  target: string
411
411
  node: NodeDetails
412
412
  onclick: string
413
+ id: string
413
414
  listeners?: Array<{
414
415
  type: Crdp.DOMDebugger.EventListener['type']
415
416
  }>