lighthouse 9.5.0-dev.20220404 → 9.5.0-dev.20220407
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/report/bundle.esm.js +1 -2
- package/dist/report/flow.js +2 -2
- package/dist/report/standalone.js +2 -2
- package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +0 -1
- package/lighthouse-core/audits/byte-efficiency/unminified-css.js +3 -5
- package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +3 -2
- package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +0 -1
- package/lighthouse-core/audits/script-treemap-data.js +2 -2
- package/lighthouse-core/audits/seo/link-text.js +2 -0
- package/lighthouse-core/audits/service-worker.js +7 -2
- package/lighthouse-core/computed/main-resource.js +3 -2
- package/lighthouse-core/computed/page-dependency-graph.js +1 -0
- package/lighthouse-core/computed/resource-summary.js +5 -5
- package/lighthouse-core/computed/unused-css.js +6 -7
- package/lighthouse-core/config/budget.js +2 -2
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +1 -5
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -3
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +0 -2
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +0 -2
- package/lighthouse-core/lib/script-helpers.js +9 -1
- package/lighthouse-core/runner.js +0 -5
- package/package.json +1 -1
- package/report/generator/report-generator.js +1 -1
- package/report/renderer/open-tab.js +1 -2
- package/types/artifacts.d.ts +3 -4
- package/types/lhr/lhr.d.ts +2 -2
- package/types/lhr/treemap.d.ts +0 -1
|
@@ -4999,7 +4999,7 @@ function computeWindowNameSuffix(json) {
|
|
|
4999
4999
|
// @ts-expect-error - If this is a v2 LHR, use old `generatedTime`.
|
|
5000
5000
|
const fallbackFetchTime = /** @type {string} */ (json.generatedTime);
|
|
5001
5001
|
const fetchTime = json.fetchTime || fallbackFetchTime;
|
|
5002
|
-
return `${json.lighthouseVersion}-${json.
|
|
5002
|
+
return `${json.lighthouseVersion}-${json.finalUrl}-${fetchTime}`;
|
|
5003
5003
|
}
|
|
5004
5004
|
|
|
5005
5005
|
/**
|
|
@@ -5081,7 +5081,6 @@ function openTreemap(json) {
|
|
|
5081
5081
|
/** @type {LH.Treemap.Options} */
|
|
5082
5082
|
const treemapOptions = {
|
|
5083
5083
|
lhr: {
|
|
5084
|
-
requestedUrl: json.requestedUrl,
|
|
5085
5084
|
finalUrl: json.finalUrl,
|
|
5086
5085
|
audits: {
|
|
5087
5086
|
'script-treemap-data': json.audits['script-treemap-data'],
|
package/dist/report/flow.js
CHANGED
|
@@ -188,7 +188,7 @@ class ja{constructor(e,a){this._document=e,this._lighthouseChannel="unknown",thi
|
|
|
188
188
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
189
189
|
* See the License for the specific language governing permissions and
|
|
190
190
|
* limitations under the License.
|
|
191
|
-
*/function cn(){const e=window.location.host.endsWith(".vercel.app"),a=new URLSearchParams(window.location.search).has("dev");return e?`https://${window.location.host}/gh-pages`:a?"http://localhost:8000":"https://googlechrome.github.io/lighthouse"}function dn(e){const a=e.generatedTime,n=e.fetchTime||a;return`${e.lighthouseVersion}-${e.
|
|
191
|
+
*/function cn(){const e=window.location.host.endsWith(".vercel.app"),a=new URLSearchParams(window.location.search).has("dev");return e?`https://${window.location.host}/gh-pages`:a?"http://localhost:8000":"https://googlechrome.github.io/lighthouse"}function dn(e){const a=e.generatedTime,n=e.fetchTime||a;return`${e.lighthouseVersion}-${e.finalUrl}-${n}`}async function mn(e,a,n){const t=new URL(a),i=Boolean(window.CompressionStream);t.hash=await un.toBase64(JSON.stringify(e),{gzip:i}),i&&t.searchParams.set("gzip","1"),window.open(t.toString(),n)}async function hn(e){const a="viewer-"+dn(e);!function(e,a,n){const t=new URL(a).origin;window.addEventListener("message",(function a(n){n.origin===t&&i&&n.data.opened&&(i.postMessage(e,t),window.removeEventListener("message",a))}));const i=window.open(a,n)}({lhr:e},cn()+"/viewer/",a)}
|
|
192
192
|
/**
|
|
193
193
|
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
|
|
194
194
|
* 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
|
|
@@ -220,7 +220,7 @@ class gn{constructor(e){this._dom=e,this._toggleEl,this._menuEl,this.onDocumentK
|
|
|
220
220
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
221
221
|
* See the License for the specific language governing permissions and
|
|
222
222
|
* limitations under the License.
|
|
223
|
-
*/class yn{constructor(e,a={}){this.json,this._dom=e,this._opts=a,this._topbar=a.omitTopbar?null:new vn(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const a=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!a&&window.matchMedia("(prefers-color-scheme: dark)").matches&&sn(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((a=>{const n=e.categories[a];return n&&1===n.score})),t=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!t&&(this._enableFireworks(),a||sn(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((a=>Boolean("metrics"===a.group&&e.audits[a.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:de.i18n.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");mn({lhr:{
|
|
223
|
+
*/class yn{constructor(e,a={}){this.json,this._dom=e,this._opts=a,this._topbar=a.omitTopbar?null:new vn(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const a=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!a&&window.matchMedia("(prefers-color-scheme: dark)").matches&&sn(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((a=>{const n=e.categories[a];return n&&1===n.score})),t=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!t&&(this._enableFireworks(),a||sn(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((a=>Boolean("metrics"===a.group&&e.audits[a.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:de.i18n.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");mn({lhr:{finalUrl:e.finalUrl,audits:{"script-treemap-data":e.audits["script-treemap-data"]},configSettings:{locale:e.configSettings.locale}}},cn()+"/treemap/","treemap-"+dn(e))}(this.json)}),this._opts.onViewTrace&&this.addButton({text:"simulate"===e.configSettings.throttlingMethod?de.i18n.strings.viewOriginalTraceLabel:de.i18n.strings.viewTraceLabel,onClick:()=>this._opts.onViewTrace?.()}),this._opts.getStandaloneReportHTML&&this._dom.find('a[data-action="save-html"]',this._dom.rootEl).classList.remove("lh-hidden");for(const e of this._dom.findAll("[data-i18n]",this._dom.rootEl)){const a=e.getAttribute("data-i18n");e.textContent=de.i18n.strings[a]}}addButton(e){const a=this._dom.rootEl.querySelector(".lh-audit-group--metrics");if(!a)return;let n=a.querySelector(".lh-buttons");n||(n=this._dom.createChildOf(a,"div","lh-buttons"));const t=["lh-button"];e.icon&&(t.push("lh-report-icon"),t.push(`lh-report-icon--${e.icon}`));const i=this._dom.createChildOf(n,"button",t.join(" "));return i.textContent=e.text,i.addEventListener("click",e.onClick),i}resetUIState(){this._topbar&&this._topbar.resetUIState()}getReportHtml(){if(!this._opts.getStandaloneReportHTML)throw new Error("`getStandaloneReportHTML` is not set");return this.resetUIState(),this._opts.getStandaloneReportHTML()}saveAsGist(){throw new Error("Cannot save as gist from base report")}_enableFireworks(){this._dom.find(".lh-scores-container",this._dom.rootEl).classList.add("lh-score100")}_setupMediaQueryListeners(){const e=self.matchMedia("(max-width: 500px)");e.addListener(this.onMediaQueryChange),this.onMediaQueryChange(e)}_resetUIState(){this._topbar&&this._topbar.resetUIState()}onMediaQueryChange(e){this._dom.rootEl.classList.toggle("lh-narrow",e.matches)}_setupThirdPartyFilter(){const e=["uses-rel-preconnect","third-party-facades"],a=["legacy-javascript"];Array.from(this._dom.rootEl.querySelectorAll("table.lh-table")).filter((e=>e.querySelector("td.lh-table-column--url, td.lh-table-column--source-location"))).filter((a=>{const n=a.closest(".lh-audit");if(!n)throw new Error(".lh-table not within audit");return!e.includes(n.id)})).forEach((e=>{const n=function(e){return Array.from(e.tBodies[0].rows)}(e),t=this._getThirdPartyRows(n,this.json.finalUrl),i=this._dom.createComponent("3pFilter"),o=this._dom.find("input",i);o.addEventListener("change",(e=>{const a=e.target instanceof HTMLInputElement&&!e.target.checked;let i=!0,o=n[0];for(;o;){const e=a&&t.includes(o);do{o.classList.toggle("lh-row--hidden",e),o.classList.toggle("lh-row--even",!e&&i),o.classList.toggle("lh-row--odd",!e&&!i),o=o.nextElementSibling}while(o&&o.classList.contains("lh-sub-item-row"));e||(i=!i)}})),this._dom.find(".lh-3p-filter-count",i).textContent=`${t.length}`,this._dom.find(".lh-3p-ui-string",i).textContent=de.i18n.strings.thirdPartyResourcesLabel;const r=t.length===n.length,s=!t.length;if((r||s)&&(this._dom.find("div.lh-3p-filter",i).hidden=!0),!e.parentNode)return;e.parentNode.insertBefore(i,e);const l=e.closest(".lh-audit");if(!l)throw new Error(".lh-table not within audit");a.includes(l.id)&&!r&&o.click()}))}_setupElementScreenshotOverlay(e){const a=this.json.audits["full-page-screenshot"]&&this.json.audits["full-page-screenshot"].details&&"full-page-screenshot"===this.json.audits["full-page-screenshot"].details.type&&this.json.audits["full-page-screenshot"].details;a&&en.installOverlayFeature({dom:this._dom,rootEl:e,overlayContainerEl:e,fullPageScreenshot:a})}_getThirdPartyRows(e,a){const n=[],t=de.getRootDomain(a);for(const a of e){if(a.classList.contains("lh-sub-item-row"))continue;const e=a.querySelector("div.lh-text__url");if(!e)continue;const i=e.dataset.url;if(!i)continue;de.getRootDomain(i)!==t&&n.push(a)}return n}_saveFile(e){const a=e.type.match("json")?".json":".html",n=bn.getLhrFilenamePrefix(this.json)+a;this._opts.onSaveFileOverride?this._opts.onSaveFileOverride(e,n):this._dom.saveFile(e,n)}}
|
|
224
224
|
/**
|
|
225
225
|
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
|
|
226
226
|
* 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
|
|
@@ -171,7 +171,7 @@ class s{constructor(e,t){this._document=e,this._lighthouseChannel="unknown",this
|
|
|
171
171
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
172
172
|
* See the License for the specific language governing permissions and
|
|
173
173
|
* limitations under the License.
|
|
174
|
-
*/function P(){const e=window.location.host.endsWith(".vercel.app"),t=new URLSearchParams(window.location.search).has("dev");return e?`https://${window.location.host}/gh-pages`:t?"http://localhost:8000":"https://googlechrome.github.io/lighthouse"}function U(e){const t=e.generatedTime,n=e.fetchTime||t;return`${e.lighthouseVersion}-${e.
|
|
174
|
+
*/function P(){const e=window.location.host.endsWith(".vercel.app"),t=new URLSearchParams(window.location.search).has("dev");return e?`https://${window.location.host}/gh-pages`:t?"http://localhost:8000":"https://googlechrome.github.io/lighthouse"}function U(e){const t=e.generatedTime,n=e.fetchTime||t;return`${e.lighthouseVersion}-${e.finalUrl}-${n}`}async function $(e,t,n){const r=new URL(t),o=Boolean(window.CompressionStream);r.hash=await R.toBase64(JSON.stringify(e),{gzip:o}),o&&r.searchParams.set("gzip","1"),window.open(r.toString(),n)}async function O(e){const t="viewer-"+U(e);!function(e,t,n){const r=new URL(t).origin;window.addEventListener("message",(function t(n){n.origin===r&&o&&n.data.opened&&(o.postMessage(e,r),window.removeEventListener("message",t))}));const o=window.open(t,n)}({lhr:e},P()+"/viewer/",t)}
|
|
175
175
|
/**
|
|
176
176
|
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
|
|
177
177
|
* 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
|
|
@@ -203,7 +203,7 @@ class B{constructor(e){this._dom=e,this._toggleEl,this._menuEl,this.onDocumentKe
|
|
|
203
203
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
204
204
|
* See the License for the specific language governing permissions and
|
|
205
205
|
* limitations under the License.
|
|
206
|
-
*/class j{constructor(e,t={}){this.json,this._dom=e,this._opts=t,this._topbar=t.omitTopbar?null:new I(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const t=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!t&&window.matchMedia("(prefers-color-scheme: dark)").matches&&N(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((t=>{const n=e.categories[t];return n&&1===n.score})),r=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!r&&(this._enableFireworks(),t||N(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((t=>Boolean("metrics"===t.group&&e.audits[t.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:l.i18n.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");$({lhr:{
|
|
206
|
+
*/class j{constructor(e,t={}){this.json,this._dom=e,this._opts=t,this._topbar=t.omitTopbar?null:new I(this,e),this.onMediaQueryChange=this.onMediaQueryChange.bind(this)}initFeatures(e){this.json=e,this._topbar&&(this._topbar.enable(e),this._topbar.resetUIState()),this._setupMediaQueryListeners(),this._setupThirdPartyFilter(),this._setupElementScreenshotOverlay(this._dom.rootEl);const t=this._dom.isDevTools()||this._opts.disableDarkMode||this._opts.disableAutoDarkModeAndFireworks;!t&&window.matchMedia("(prefers-color-scheme: dark)").matches&&N(this._dom,!0);const n=["performance","accessibility","best-practices","seo"].every((t=>{const n=e.categories[t];return n&&1===n.score})),r=this._opts.disableFireworks||this._opts.disableAutoDarkModeAndFireworks;n&&!r&&(this._enableFireworks(),t||N(this._dom,!0));if(e.categories.performance&&e.categories.performance.auditRefs.some((t=>Boolean("metrics"===t.group&&e.audits[t.id].errorMessage)))){this._dom.find("input.lh-metrics-toggle__input",this._dom.rootEl).checked=!0}this.json.audits["script-treemap-data"]&&this.json.audits["script-treemap-data"].details&&this.addButton({text:l.i18n.strings.viewTreemapLabel,icon:"treemap",onClick:()=>function(e){if(!e.audits["script-treemap-data"].details)throw new Error("no script treemap data found");$({lhr:{finalUrl:e.finalUrl,audits:{"script-treemap-data":e.audits["script-treemap-data"]},configSettings:{locale:e.configSettings.locale}}},P()+"/treemap/","treemap-"+U(e))}(this.json)}),this._opts.onViewTrace&&this.addButton({text:"simulate"===e.configSettings.throttlingMethod?l.i18n.strings.viewOriginalTraceLabel:l.i18n.strings.viewTraceLabel,onClick:()=>this._opts.onViewTrace?.()}),this._opts.getStandaloneReportHTML&&this._dom.find('a[data-action="save-html"]',this._dom.rootEl).classList.remove("lh-hidden");for(const e of this._dom.findAll("[data-i18n]",this._dom.rootEl)){const t=e.getAttribute("data-i18n");e.textContent=l.i18n.strings[t]}}addButton(e){const t=this._dom.rootEl.querySelector(".lh-audit-group--metrics");if(!t)return;let n=t.querySelector(".lh-buttons");n||(n=this._dom.createChildOf(t,"div","lh-buttons"));const r=["lh-button"];e.icon&&(r.push("lh-report-icon"),r.push(`lh-report-icon--${e.icon}`));const o=this._dom.createChildOf(n,"button",r.join(" "));return o.textContent=e.text,o.addEventListener("click",e.onClick),o}resetUIState(){this._topbar&&this._topbar.resetUIState()}getReportHtml(){if(!this._opts.getStandaloneReportHTML)throw new Error("`getStandaloneReportHTML` is not set");return this.resetUIState(),this._opts.getStandaloneReportHTML()}saveAsGist(){throw new Error("Cannot save as gist from base report")}_enableFireworks(){this._dom.find(".lh-scores-container",this._dom.rootEl).classList.add("lh-score100")}_setupMediaQueryListeners(){const e=self.matchMedia("(max-width: 500px)");e.addListener(this.onMediaQueryChange),this.onMediaQueryChange(e)}_resetUIState(){this._topbar&&this._topbar.resetUIState()}onMediaQueryChange(e){this._dom.rootEl.classList.toggle("lh-narrow",e.matches)}_setupThirdPartyFilter(){const e=["uses-rel-preconnect","third-party-facades"],t=["legacy-javascript"];Array.from(this._dom.rootEl.querySelectorAll("table.lh-table")).filter((e=>e.querySelector("td.lh-table-column--url, td.lh-table-column--source-location"))).filter((t=>{const n=t.closest(".lh-audit");if(!n)throw new Error(".lh-table not within audit");return!e.includes(n.id)})).forEach((e=>{const n=function(e){return Array.from(e.tBodies[0].rows)}(e),r=this._getThirdPartyRows(n,this.json.finalUrl),o=this._dom.createComponent("3pFilter"),i=this._dom.find("input",o);i.addEventListener("change",(e=>{const t=e.target instanceof HTMLInputElement&&!e.target.checked;let o=!0,i=n[0];for(;i;){const e=t&&r.includes(i);do{i.classList.toggle("lh-row--hidden",e),i.classList.toggle("lh-row--even",!e&&o),i.classList.toggle("lh-row--odd",!e&&!o),i=i.nextElementSibling}while(i&&i.classList.contains("lh-sub-item-row"));e||(o=!o)}})),this._dom.find(".lh-3p-filter-count",o).textContent=`${r.length}`,this._dom.find(".lh-3p-ui-string",o).textContent=l.i18n.strings.thirdPartyResourcesLabel;const a=r.length===n.length,s=!r.length;if((a||s)&&(this._dom.find("div.lh-3p-filter",o).hidden=!0),!e.parentNode)return;e.parentNode.insertBefore(o,e);const c=e.closest(".lh-audit");if(!c)throw new Error(".lh-table not within audit");t.includes(c.id)&&!a&&i.click()}))}_setupElementScreenshotOverlay(e){const t=this.json.audits["full-page-screenshot"]&&this.json.audits["full-page-screenshot"].details&&"full-page-screenshot"===this.json.audits["full-page-screenshot"].details.type&&this.json.audits["full-page-screenshot"].details;t&&A.installOverlayFeature({dom:this._dom,rootEl:e,overlayContainerEl:e,fullPageScreenshot:t})}_getThirdPartyRows(e,t){const n=[],r=l.getRootDomain(t);for(const t of e){if(t.classList.contains("lh-sub-item-row"))continue;const e=t.querySelector("div.lh-text__url");if(!e)continue;const o=e.dataset.url;if(!o)continue;l.getRootDomain(o)!==r&&n.push(t)}return n}_saveFile(e){const t=e.type.match("json")?".json":".html",n=G.getLhrFilenamePrefix(this.json)+t;this._opts.onSaveFileOverride?this._opts.onSaveFileOverride(e,n):this._dom.saveFile(e,n)}}
|
|
207
207
|
/**
|
|
208
208
|
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
|
|
209
209
|
* 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
|
|
@@ -262,7 +262,6 @@ class RenderBlockingResources extends Audit {
|
|
|
262
262
|
try {
|
|
263
263
|
const unusedCssItems = await UnusedCSS.request({
|
|
264
264
|
CSSUsage: artifacts.CSSUsage,
|
|
265
|
-
URL: artifacts.URL,
|
|
266
265
|
devtoolsLog: artifacts.devtoolsLogs[Audit.DEFAULT_PASS],
|
|
267
266
|
}, context);
|
|
268
267
|
for (const item of unusedCssItems) {
|
|
@@ -53,15 +53,14 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
|
|
|
53
53
|
/**
|
|
54
54
|
* @param {LH.Artifacts.CSSStyleSheetInfo} stylesheet
|
|
55
55
|
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
56
|
-
* @param {string} pageUrl
|
|
57
56
|
* @return {{url: string, totalBytes: number, wastedBytes: number, wastedPercent: number}}
|
|
58
57
|
*/
|
|
59
|
-
static computeWaste(stylesheet, networkRecord
|
|
58
|
+
static computeWaste(stylesheet, networkRecord) {
|
|
60
59
|
const content = stylesheet.content;
|
|
61
60
|
const totalTokenLength = UnminifiedCSS.computeTokenLength(content);
|
|
62
61
|
|
|
63
62
|
let url = stylesheet.header.sourceURL;
|
|
64
|
-
if (!url ||
|
|
63
|
+
if (!url || stylesheet.header.isInline) {
|
|
65
64
|
const contentPreview = UnusedCSS.determineContentPreview(stylesheet.content);
|
|
66
65
|
url = contentPreview;
|
|
67
66
|
}
|
|
@@ -85,14 +84,13 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
|
|
|
85
84
|
* @return {ByteEfficiencyAudit.ByteEfficiencyProduct}
|
|
86
85
|
*/
|
|
87
86
|
static audit_(artifacts, networkRecords) {
|
|
88
|
-
const pageUrl = artifacts.URL.finalUrl;
|
|
89
87
|
const items = [];
|
|
90
88
|
for (const stylesheet of artifacts.CSSUsage.stylesheets) {
|
|
91
89
|
const networkRecord = networkRecords
|
|
92
90
|
.find(record => record.url === stylesheet.header.sourceURL);
|
|
93
91
|
if (!stylesheet.content) continue;
|
|
94
92
|
|
|
95
|
-
const result = UnminifiedCSS.computeWaste(stylesheet, networkRecord
|
|
93
|
+
const result = UnminifiedCSS.computeWaste(stylesheet, networkRecord);
|
|
96
94
|
|
|
97
95
|
// If the ratio is minimal, the file is likely already minified, so ignore it.
|
|
98
96
|
// If the total number of bytes to be saved is quite small, it's also safe to ignore.
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const ByteEfficiencyAudit = require('./byte-efficiency-audit.js');
|
|
9
9
|
const i18n = require('../../lib/i18n/i18n.js');
|
|
10
10
|
const computeTokenLength = require('../../lib/minification-estimator.js').computeJSTokenLength;
|
|
11
|
-
const {getRequestForScript} = require('../../lib/script-helpers.js');
|
|
11
|
+
const {getRequestForScript, isInline} = require('../../lib/script-helpers.js');
|
|
12
12
|
|
|
13
13
|
const UIStrings = {
|
|
14
14
|
/** Imperative title of a Lighthouse audit that tells the user to minify the page’s JS code to reduce file size. This is displayed in a list of audit titles that Lighthouse generates. */
|
|
@@ -83,7 +83,8 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
|
|
|
83
83
|
if (!script.content) continue;
|
|
84
84
|
|
|
85
85
|
const networkRecord = getRequestForScript(networkRecords, script);
|
|
86
|
-
|
|
86
|
+
|
|
87
|
+
const displayUrl = isInline(script) ?
|
|
87
88
|
`inline: ${script.content.substring(0, 40)}...` :
|
|
88
89
|
script.url;
|
|
89
90
|
try {
|
|
@@ -47,7 +47,6 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
|
|
|
47
47
|
static async audit_(artifacts, _, context) {
|
|
48
48
|
const unusedCssItems = await UnusedCSS.request({
|
|
49
49
|
CSSUsage: artifacts.CSSUsage,
|
|
50
|
-
URL: artifacts.URL,
|
|
51
50
|
devtoolsLog: artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS],
|
|
52
51
|
}, context);
|
|
53
52
|
const items = unusedCssItems
|
|
@@ -19,6 +19,7 @@ const Audit = require('./audit.js');
|
|
|
19
19
|
const JsBundles = require('../computed/js-bundles.js');
|
|
20
20
|
const UnusedJavaScriptSummary = require('../computed/unused-javascript-summary.js');
|
|
21
21
|
const ModuleDuplication = require('../computed/module-duplication.js');
|
|
22
|
+
const {isInline} = require('../lib/script-helpers.js');
|
|
22
23
|
|
|
23
24
|
class ScriptTreemapDataAudit extends Audit {
|
|
24
25
|
/**
|
|
@@ -236,8 +237,7 @@ class ScriptTreemapDataAudit extends Audit {
|
|
|
236
237
|
|
|
237
238
|
// If this is an inline script, place the node inside a top-level (aka depth-one) node.
|
|
238
239
|
// Also separate each iframe / the main page's inline scripts into their own top-level nodes.
|
|
239
|
-
|
|
240
|
-
if (isInlineHtmlScript) {
|
|
240
|
+
if (isInline(script)) {
|
|
241
241
|
let htmlNode = htmlNodesByFrameId.get(script.executionContextAuxData.frameId);
|
|
242
242
|
if (!htmlNode) {
|
|
243
243
|
htmlNode = {
|
|
@@ -119,6 +119,8 @@ class LinkText extends Audit {
|
|
|
119
119
|
if (
|
|
120
120
|
href.startsWith('javascript:') ||
|
|
121
121
|
href.startsWith('mailto:') ||
|
|
122
|
+
// This line prevents the audit from flagging anchor links.
|
|
123
|
+
// In this case it is better to use `finalUrl` than `mainDocumentUrl`.
|
|
122
124
|
URL.equalWithExcludedFragments(link.href, artifacts.URL.finalUrl)
|
|
123
125
|
) {
|
|
124
126
|
return false;
|
|
@@ -51,6 +51,7 @@ class ServiceWorker extends Audit {
|
|
|
51
51
|
title: str_(UIStrings.title),
|
|
52
52
|
failureTitle: str_(UIStrings.failureTitle),
|
|
53
53
|
description: str_(UIStrings.description),
|
|
54
|
+
supportedModes: ['navigation'],
|
|
54
55
|
requiredArtifacts: ['URL', 'ServiceWorker', 'WebAppManifest'],
|
|
55
56
|
};
|
|
56
57
|
}
|
|
@@ -128,8 +129,12 @@ class ServiceWorker extends Audit {
|
|
|
128
129
|
* @return {LH.Audit.Product}
|
|
129
130
|
*/
|
|
130
131
|
static audit(artifacts) {
|
|
131
|
-
// Match against artifacts.URL.
|
|
132
|
-
|
|
132
|
+
// Match against `artifacts.URL.mainDocumentUrl` so audit accounts for any redirects.
|
|
133
|
+
// Service workers won't control network requests if the page uses `history.pushState` to "enter" the SW scope.
|
|
134
|
+
// For this reason it is better to evaluate the SW in relation to the main document url rather than the final frame url.
|
|
135
|
+
const {mainDocumentUrl} = artifacts.URL;
|
|
136
|
+
if (!mainDocumentUrl) throw new Error('mainDocumentUrl must exist in navigation mode');
|
|
137
|
+
const pageUrl = new URL(mainDocumentUrl);
|
|
133
138
|
const {versions, registrations} = artifacts.ServiceWorker;
|
|
134
139
|
|
|
135
140
|
const versionsForOrigin = ServiceWorker.getVersionsForOrigin(versions, pageUrl);
|
|
@@ -20,9 +20,10 @@ class MainResource {
|
|
|
20
20
|
* @return {Promise<LH.Artifacts.NetworkRequest>}
|
|
21
21
|
*/
|
|
22
22
|
static async compute_(data, context) {
|
|
23
|
-
const
|
|
23
|
+
const {mainDocumentUrl} = data.URL;
|
|
24
|
+
if (!mainDocumentUrl) throw new Error('mainDocumentUrl must exist to get the main resource');
|
|
24
25
|
const requests = await NetworkRecords.request(data.devtoolsLog, context);
|
|
25
|
-
const mainResource = NetworkAnalyzer.findResourceForUrl(requests,
|
|
26
|
+
const mainResource = NetworkAnalyzer.findResourceForUrl(requests, mainDocumentUrl);
|
|
26
27
|
if (!mainResource) {
|
|
27
28
|
throw new Error('Unable to identify the main resource');
|
|
28
29
|
}
|
|
@@ -396,6 +396,7 @@ class PageDependencyGraph {
|
|
|
396
396
|
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords);
|
|
397
397
|
const cpuNodes = PageDependencyGraph.getCPUNodes(processedTrace);
|
|
398
398
|
const {requestedUrl, mainDocumentUrl} = URL;
|
|
399
|
+
if (!requestedUrl) throw new Error('requestedUrl is required to get the root request');
|
|
399
400
|
if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource');
|
|
400
401
|
|
|
401
402
|
const rootRequest = NetworkAnalyzer.findResourceForUrl(networkRecords, requestedUrl);
|
|
@@ -35,11 +35,11 @@ class ResourceSummary {
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
38
|
-
* @param {
|
|
38
|
+
* @param {LH.Artifacts.URL} URLArtifact
|
|
39
39
|
* @param {ImmutableObject<LH.Budget[]|null>} budgets
|
|
40
40
|
* @return {Record<LH.Budget.ResourceType, ResourceEntry>}
|
|
41
41
|
*/
|
|
42
|
-
static summarize(networkRecords,
|
|
42
|
+
static summarize(networkRecords, URLArtifact, budgets) {
|
|
43
43
|
/** @type {Record<LH.Budget.ResourceType, ResourceEntry>} */
|
|
44
44
|
const resourceSummary = {
|
|
45
45
|
'stylesheet': {count: 0, resourceSize: 0, transferSize: 0},
|
|
@@ -52,13 +52,13 @@ class ResourceSummary {
|
|
|
52
52
|
'total': {count: 0, resourceSize: 0, transferSize: 0},
|
|
53
53
|
'third-party': {count: 0, resourceSize: 0, transferSize: 0},
|
|
54
54
|
};
|
|
55
|
-
const budget = Budget.getMatchingBudget(budgets,
|
|
55
|
+
const budget = Budget.getMatchingBudget(budgets, URLArtifact.mainDocumentUrl);
|
|
56
56
|
/** @type {ReadonlyArray<string>} */
|
|
57
57
|
let firstPartyHosts = [];
|
|
58
58
|
if (budget?.options?.firstPartyHostnames) {
|
|
59
59
|
firstPartyHosts = budget.options.firstPartyHostnames;
|
|
60
60
|
} else {
|
|
61
|
-
const rootDomain = Util.getRootDomain(
|
|
61
|
+
const rootDomain = Util.getRootDomain(URLArtifact.finalUrl);
|
|
62
62
|
firstPartyHosts = [`*.${rootDomain}`];
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -107,7 +107,7 @@ class ResourceSummary {
|
|
|
107
107
|
*/
|
|
108
108
|
static async compute_(data, context) {
|
|
109
109
|
const networkRecords = await NetworkRecords.request(data.devtoolsLog, context);
|
|
110
|
-
return ResourceSummary.summarize(networkRecords, data.URL
|
|
110
|
+
return ResourceSummary.summarize(networkRecords, data.URL, data.budgets);
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
113
|
|
|
@@ -120,12 +120,11 @@ class UnusedCSS {
|
|
|
120
120
|
|
|
121
121
|
/**
|
|
122
122
|
* @param {StyleSheetInfo} stylesheetInfo The stylesheetInfo object.
|
|
123
|
-
* @param {string} pageUrl The URL of the page, used to identify inline styles.
|
|
124
123
|
* @return {LH.Audit.ByteEfficiencyItem}
|
|
125
124
|
*/
|
|
126
|
-
static mapSheetToResult(stylesheetInfo
|
|
125
|
+
static mapSheetToResult(stylesheetInfo) {
|
|
127
126
|
let url = stylesheetInfo.header.sourceURL;
|
|
128
|
-
if (!url ||
|
|
127
|
+
if (!url || stylesheetInfo.header.isInline) {
|
|
129
128
|
const contentPreview = UnusedCSS.determineContentPreview(stylesheetInfo.content);
|
|
130
129
|
url = contentPreview;
|
|
131
130
|
}
|
|
@@ -135,20 +134,20 @@ class UnusedCSS {
|
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
/**
|
|
138
|
-
* @param {{CSSUsage: LH.Artifacts['CSSUsage'],
|
|
137
|
+
* @param {{CSSUsage: LH.Artifacts['CSSUsage'], devtoolsLog: LH.DevtoolsLog}} data
|
|
139
138
|
* @param {LH.Artifacts.ComputedContext} context
|
|
140
139
|
* @return {Promise<LH.Audit.ByteEfficiencyItem[]>}
|
|
141
140
|
*/
|
|
142
141
|
static async compute_(data, context) {
|
|
143
|
-
const {CSSUsage,
|
|
142
|
+
const {CSSUsage, devtoolsLog} = data;
|
|
144
143
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
145
144
|
const indexedSheets = UnusedCSS.indexStylesheetsById(CSSUsage.stylesheets, networkRecords);
|
|
146
145
|
UnusedCSS.indexUsedRules(CSSUsage.rules, indexedSheets);
|
|
147
146
|
|
|
148
147
|
const items = Object.keys(indexedSheets)
|
|
149
|
-
.map(sheetId => UnusedCSS.mapSheetToResult(indexedSheets[sheetId]
|
|
148
|
+
.map(sheetId => UnusedCSS.mapSheetToResult(indexedSheets[sheetId]));
|
|
150
149
|
return items;
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
module.exports = makeComputedArtifact(UnusedCSS, ['CSSUsage', '
|
|
153
|
+
module.exports = makeComputedArtifact(UnusedCSS, ['CSSUsage', 'devtoolsLog']);
|
|
@@ -137,11 +137,11 @@ class Budget {
|
|
|
137
137
|
* If multiple budgets match based on thier 'path' property,
|
|
138
138
|
* then the last-listed of those budgets is returned.
|
|
139
139
|
* @param {Immutable<Array<LH.Budget>>|null} budgets
|
|
140
|
-
* @param {string} url
|
|
140
|
+
* @param {string|undefined} url
|
|
141
141
|
* @return {Immutable<LH.Budget> | undefined} budget
|
|
142
142
|
*/
|
|
143
143
|
static getMatchingBudget(budgets, url) {
|
|
144
|
-
if (budgets === null) return;
|
|
144
|
+
if (budgets === null || url === undefined) return;
|
|
145
145
|
|
|
146
146
|
// Applies the LAST matching budget.
|
|
147
147
|
for (let i = budgets.length - 1; i >= 0; i--) {
|
|
@@ -35,10 +35,8 @@ async function getBaseArtifacts(config, driver, context) {
|
|
|
35
35
|
HostFormFactor: userAgent.includes('Android') || userAgent.includes('Mobile') ?
|
|
36
36
|
'mobile' : 'desktop',
|
|
37
37
|
// Contextual artifacts whose collection changes based on gather mode.
|
|
38
|
-
// TODO: Make `requestedUrl` optional in timespan and snapshot modes.
|
|
39
38
|
URL: {
|
|
40
39
|
initialUrl: '',
|
|
41
|
-
requestedUrl: '',
|
|
42
40
|
finalUrl: '',
|
|
43
41
|
},
|
|
44
42
|
PageLoadError: null,
|
|
@@ -88,13 +86,11 @@ function finalizeArtifacts(baseArtifacts, gathererArtifacts) {
|
|
|
88
86
|
artifacts.LighthouseRunWarnings = deduplicateWarnings(warnings);
|
|
89
87
|
|
|
90
88
|
if (artifacts.PageLoadError && !artifacts.URL.finalUrl) {
|
|
91
|
-
artifacts.URL.finalUrl = artifacts.URL.requestedUrl;
|
|
89
|
+
artifacts.URL.finalUrl = artifacts.URL.requestedUrl || artifacts.URL.initialUrl;
|
|
92
90
|
}
|
|
93
91
|
|
|
94
92
|
// Check that the runner remembered to mutate the special-case URL artifact.
|
|
95
|
-
// TODO: Make `requestedUrl` optional.
|
|
96
93
|
if (!artifacts.URL.initialUrl) throw new Error('Runner did not set initialUrl');
|
|
97
|
-
if (!artifacts.URL.requestedUrl) throw new Error('Runner did not set requestedUrl');
|
|
98
94
|
if (!artifacts.URL.finalUrl) throw new Error('Runner did not set finalUrl');
|
|
99
95
|
|
|
100
96
|
return artifacts;
|
|
@@ -229,9 +229,8 @@ async function _navigation(navigationContext) {
|
|
|
229
229
|
const navigateResult = await _navigate(navigationContext);
|
|
230
230
|
|
|
231
231
|
// Every required url is initialized to an empty string in `getBaseArtifacts`.
|
|
232
|
-
// If we haven't set the required urls yet, set them here.
|
|
233
|
-
|
|
234
|
-
if (!URL.finalUrl || !URL.initialUrl) {
|
|
232
|
+
// If we haven't set all the required urls yet, set them here.
|
|
233
|
+
if (!Object.values(phaseState.baseArtifacts).every(Boolean)) {
|
|
235
234
|
phaseState.baseArtifacts.URL = {
|
|
236
235
|
initialUrl,
|
|
237
236
|
requestedUrl: navigateResult.requestedUrl,
|
|
@@ -38,8 +38,6 @@ async function snapshotGather(options) {
|
|
|
38
38
|
const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'snapshot'});
|
|
39
39
|
baseArtifacts.URL = {
|
|
40
40
|
initialUrl: url,
|
|
41
|
-
// TODO: Remove `requestedUrl` from snapshot mode.
|
|
42
|
-
requestedUrl: url,
|
|
43
41
|
finalUrl: url,
|
|
44
42
|
};
|
|
45
43
|
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* @param {LH.Artifacts.Script} script
|
|
10
|
+
* @return {boolean}
|
|
11
|
+
*/
|
|
12
|
+
function isInline(script) {
|
|
13
|
+
return Boolean(script.startLine || script.startColumn);
|
|
14
|
+
}
|
|
15
|
+
|
|
8
16
|
/**
|
|
9
17
|
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
|
|
10
18
|
* @param {LH.Artifacts.Script} script
|
|
@@ -18,4 +26,4 @@ function getRequestForScript(networkRecords, script) {
|
|
|
18
26
|
return networkRequest;
|
|
19
27
|
}
|
|
20
28
|
|
|
21
|
-
module.exports = {getRequestForScript};
|
|
29
|
+
module.exports = {getRequestForScript, isInline};
|
|
@@ -158,11 +158,6 @@ class Runner {
|
|
|
158
158
|
// No browser required, just load the artifacts from disk.
|
|
159
159
|
const path = this._getDataSavePath(settings);
|
|
160
160
|
artifacts = assetSaver.loadArtifacts(path);
|
|
161
|
-
const requestedUrl = artifacts.URL.requestedUrl;
|
|
162
|
-
|
|
163
|
-
if (!requestedUrl) {
|
|
164
|
-
throw new Error('Cannot run audit mode on empty URL');
|
|
165
|
-
}
|
|
166
161
|
} else {
|
|
167
162
|
const runnerStatus = {msg: 'Gather phase', id: 'lh:runner:gather'};
|
|
168
163
|
log.time(runnerStatus, 'verbose');
|
package/package.json
CHANGED
|
@@ -105,7 +105,7 @@ class ReportGenerator {
|
|
|
105
105
|
|
|
106
106
|
// First we have metadata about the LHR.
|
|
107
107
|
rows.push(rowFormatter(topLevelKeys));
|
|
108
|
-
rows.push(rowFormatter(topLevelKeys.map(key => lhr[key])));
|
|
108
|
+
rows.push(rowFormatter(topLevelKeys.map(key => lhr[key] ?? null)));
|
|
109
109
|
|
|
110
110
|
// Some spacing.
|
|
111
111
|
rows.push([]);
|
|
@@ -38,7 +38,7 @@ function computeWindowNameSuffix(json) {
|
|
|
38
38
|
// @ts-expect-error - If this is a v2 LHR, use old `generatedTime`.
|
|
39
39
|
const fallbackFetchTime = /** @type {string} */ (json.generatedTime);
|
|
40
40
|
const fetchTime = json.fetchTime || fallbackFetchTime;
|
|
41
|
-
return `${json.lighthouseVersion}-${json.
|
|
41
|
+
return `${json.lighthouseVersion}-${json.finalUrl}-${fetchTime}`;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/**
|
|
@@ -120,7 +120,6 @@ function openTreemap(json) {
|
|
|
120
120
|
/** @type {LH.Treemap.Options} */
|
|
121
121
|
const treemapOptions = {
|
|
122
122
|
lhr: {
|
|
123
|
-
requestedUrl: json.requestedUrl,
|
|
124
123
|
finalUrl: json.finalUrl,
|
|
125
124
|
audits: {
|
|
126
125
|
'script-treemap-data': json.audits['script-treemap-data'],
|
package/types/artifacts.d.ts
CHANGED
|
@@ -191,11 +191,10 @@ declare module Artifacts {
|
|
|
191
191
|
/** URL of the main frame before Lighthouse starts. */
|
|
192
192
|
initialUrl: string;
|
|
193
193
|
/**
|
|
194
|
-
* URL of the
|
|
195
|
-
* Will be
|
|
196
|
-
* TODO: Make this property `undefined` in timespan/snapshot.
|
|
194
|
+
* URL of the initially requested URL during a Lighthouse navigation.
|
|
195
|
+
* Will be `undefined` in timespan/snapshot.
|
|
197
196
|
*/
|
|
198
|
-
requestedUrl
|
|
197
|
+
requestedUrl?: string;
|
|
199
198
|
/**
|
|
200
199
|
* URL of the last document request during a Lighthouse navigation.
|
|
201
200
|
* Will be `undefined` in timespan/snapshot.
|
package/types/lhr/lhr.d.ts
CHANGED
|
@@ -13,8 +13,8 @@ import {ConfigSettings} from './settings';
|
|
|
13
13
|
interface Result {
|
|
14
14
|
/** Gather mode used to collect artifacts for this result. */
|
|
15
15
|
gatherMode: Result.GatherMode;
|
|
16
|
-
/** The URL that
|
|
17
|
-
requestedUrl
|
|
16
|
+
/** The URL that Lighthouse initially navigated to. Will be `undefined` in timespan/snapshot. */
|
|
17
|
+
requestedUrl?: string;
|
|
18
18
|
/** The post-redirects URL that Lighthouse loaded. */
|
|
19
19
|
finalUrl: string;
|
|
20
20
|
/** The ISO-8601 timestamp of when the results were generated. */
|