{"componentChunkName":"component---src-templates-blog-post-js","path":"/lighthouse-architecture-demystified/","result":{"data":{"site":{"siteMetadata":{"title":"Fabrizio Fortunato","author":"Fabrizio Fortunato","siteUrl":"https://izifortune.com"}},"markdownRemark":{"id":"1b2ee71e-3314-5640-b294-870168002b0e","excerpt":"Intro In one of my previous articles I explained how we can use Lighthouse to put our website on a budget and why monitoring your website performance its an…","html":"<h2>Intro</h2>\n<p>In one of my previous <a href=\"https://izifortune.com/performance-budgets-with-lighthouse-lighthouse-keeper/\">articles</a> I explained how we can use <em>Lighthouse</em> to put our website on a budget and why monitoring your website performance its an important aspect of web development. In this article, we will go deeper into <em>Lighthouse</em> building blocks, its architecture and learn how we can start auditing and collecting custom metrics for our web pages.</p>\n<p><em>Lighthouse</em> is an audit tool developed by Google which collects different metrics of your website. After collecting the metrics it will present a series of scores for your webpage. The scoring is divided into five main auditing areas.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/2b314f7631e1377a4aaa605051a5e34e/34524/audit1-2.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 17.567567567567565%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsSAAALEgHS3X78AAAAtklEQVQI1y1OXQuDMBDz//+vDd9ksMFwMO2craI9q/aq1n6s1eXhuISEJEnLbFql9955v3FQee4PGIStulmEk+qeaPZ0Zg+/VeNWP4ygSVpk4zIHaTdmGae5KKwx0T3UmF92OUS3tarM1Pd+ho3k+LrugiXkQ1Ci1rqqKnqgaVtrHedAijcMMSwRKaOsYWpZ4kbnOsZCRxLmBnJKLoTisX8a+rUGgB74KsRUU0SpOiLE2M4965ofPQ7jgtaUbR4AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lighthouse audit\"\n        title=\"Lighthouse audit\"\n        src=\"/dist/static/2b314f7631e1377a4aaa605051a5e34e/102e5/audit1-2.png\"\n        srcset=\"/dist/static/2b314f7631e1377a4aaa605051a5e34e/83276/audit1-2.png 148w,\n/dist/static/2b314f7631e1377a4aaa605051a5e34e/ba02f/audit1-2.png 295w,\n/dist/static/2b314f7631e1377a4aaa605051a5e34e/102e5/audit1-2.png 590w,\n/dist/static/2b314f7631e1377a4aaa605051a5e34e/74549/audit1-2.png 885w,\n/dist/static/2b314f7631e1377a4aaa605051a5e34e/7213b/audit1-2.png 1180w,\n/dist/static/2b314f7631e1377a4aaa605051a5e34e/34524/audit1-2.png 1250w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>Besides the scoring of your webpage, <em>Lighthouse</em> provides more detailed information on where to focus your efforts, what is called “opportunities”. Areas of your website which impact may lead to big performance improvement as an example the following application execution time are quite high.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/e3095872833fe71890c66ff2a7fb8a29/5254f/opportunities-1.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 52.02702702702703%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAIAAAA7N+mxAAAACXBIWXMAAAsSAAALEgHS3X78AAABdUlEQVQozz1R2ZKjMBDz/3/dvE3NVhIM4fR92wECK5OqUE3Tbqsl2ZC8Hj6WYeYzV9MiZ4YsFq4XoaV2xkWhrLHe+aCtE9oJoQWT1vi8neQ8zxgjpU1LKaJ5PPr+SZsGcb/dKKVdS3/vy88f/0dF8C6XnHJ6rSsGyXEc275j8Y113b5FfXcs36m81x34c922Dx6ZGANXbtvfL8xswG1oI5fX+g1AY0wL40rrR9MwJmjbamNJTIlxoXWlUNqgxYVEDTQiXDnngi3nvNbm2Q8+RKm09554H/phxAAohnGa5gVZKoW9EEJ9AxgS+uiggH4pBc1ScrV9uz+6Zw/Ktnu2Xa+UktfDuYBPQHGjYOTVoKFthxEFJ9YS8GIMfEwIxvnCGIxA0V3SsArPuCchlbW2lFc/Till51xKiVjrmqYdxwmWq+t5HkasJmhCB/5RAIevkDLnPM/L5/DgJqAHlEGUV2XkKwlj6n/QpgZOiBVowAIKyH6a/wG32DHRCjP+TAAAAABJRU5ErkJggg=='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lightuhouse opportunities\"\n        title=\"Lightuhouse opportunities\"\n        src=\"/dist/static/e3095872833fe71890c66ff2a7fb8a29/102e5/opportunities-1.png\"\n        srcset=\"/dist/static/e3095872833fe71890c66ff2a7fb8a29/83276/opportunities-1.png 148w,\n/dist/static/e3095872833fe71890c66ff2a7fb8a29/ba02f/opportunities-1.png 295w,\n/dist/static/e3095872833fe71890c66ff2a7fb8a29/102e5/opportunities-1.png 590w,\n/dist/static/e3095872833fe71890c66ff2a7fb8a29/74549/opportunities-1.png 885w,\n/dist/static/e3095872833fe71890c66ff2a7fb8a29/5254f/opportunities-1.png 1032w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<h2>Lighthouse architecture</h2>\n<p><em>Lighthouse</em>\n<a href=\"https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md#components--terminology\">architecture</a> is built around <a href=\"https://developer.chrome.com/devtools/docs/debugger-protocol\">Chrome Debugging\nProtocol</a> which is a set of low-level API to interact with a Chrome instance. It interfaces a Chrome instance through the <em>Driver</em>. The <em>Gatherers</em> collect data from the page using the <em>Driver</em>. The output of a <em>Gatherer</em> is an <em>Artifact</em>, a collection of grouped metrics. An <em>Artifact</em> then is used by an <em>Audit</em> to test for a metric. The <em>Audits</em> asserts and assign a score to a specific metric. The output of an <em>Audit</em> is used to generate the lighthouse report that we are familiar with.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/255cd/lighthouse-architcture.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 68.91891891891892%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAOCAIAAACgpqunAAAACXBIWXMAAAsSAAALEgHS3X78AAACQElEQVQoz21SXW/TMBTN//8BCIkHHpDGAy8IJDQNhpjYpi4qQuyjW9qUjHZNltRxbMd2/JGknKQTe+EqsSzZ95zjc08QxeTlm+Qht6wihJRlSYef0u12q7Xe/a+UUkKIuq6D22VzcJyi2TSSc8EYw4GSigthrcHVvu/3a9fhG+q5eTzz3mlrDA5wD6uzxjnTDtV732qtlJScVRCFzqZprLVYA+scFzUpGaXcOQcOY0xe8DSjtRpkO2tLWi3v0zhJS8qcs1VVTS4mcRwHwAhvyPGUhnfi9EK+/6jnf+SXKTm5FOFV/eJVPY8b37VfL5Lzy4LJXdd6eDOdTheLRWCsO5qsDj5dfvtZxImJ5u7uvn53NDucbG4TGU4bUhpt/dmv7PyqILzzziqtoXGQDZHztV6mmks3yt5lREWrmlRGN4Nh3jnZAJFfLysqjPdWwk54K0QwONR6wStKifce9vVd23ceH+yCw5AGw6pKEqLgIjx6dhsNQMoe899JgvFCSNs9FQaBMbx+e/f5JGWMEEqVhuu1GAvkAaRvNptoPo/j5WIRM8Zh70A+jhfkWyIUk7wsG9sAy1oHTgkMMA9TdRCmxZAKiHKD2n43DnkQ0QLIOZKmmKizaLYIUhiGoNyHBCnxeMlu3K9+nJLr72Dpxn5Ic6Z5fNg85nnFGK5B8Gw2Q36Df4kFCSCwObs5XEQfOJcgATQaEPdtUTD+VHpIHB4vA0w8yzLs9hBjhiF15zATa/egeZGv1w9RFCEYuLB/I978F4kXFAvxI9H7AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lighthouse architecture from https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md#components--terminology\"\n        title=\"Lighthouse architecture from https://github.com/GoogleChrome/lighthouse/blob/master/docs/architecture.md#components--terminology\"\n        src=\"/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/102e5/lighthouse-architcture.png\"\n        srcset=\"/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/83276/lighthouse-architcture.png 148w,\n/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/ba02f/lighthouse-architcture.png 295w,\n/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/102e5/lighthouse-architcture.png 590w,\n/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/74549/lighthouse-architcture.png 885w,\n/dist/static/7472e4bf3d9fad5f50d8a8aea699b43d/255cd/lighthouse-architcture.png 1105w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>We will now take a close look at two of the <em>Lighthouse</em> building blocks by creating a simple audit tracking the internal rendering of a webpage. Creating an application is necessary in order to include a custom <em>Gatherer</em> or <em>Audit</em> since it is not possible to add any custom <em>Gatherer or Audit</em> directly into the <em>Chrome</em> panel.</p>\n<p>Let’s create our project and install lighthouse as a dependency</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">mkdir custom-audit &amp;&amp; cd custom-audit\nnpm i --save lighthouse</code></pre></div>\n<p>To start auditing our website we will then create a new file <code class=\"language-text\">scan.js</code> where we will import <em>Lighthouse</em> and start scanning the webpage of choice. We will use programmatic access to <em>Lighthouse</em> by importing it inside our project</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> lighthouse <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> chromeLauncher <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'chrome-launcher'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> chrome <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> chromeLauncher<span class=\"token punctuation\">.</span><span class=\"token function\">launch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>chromeFlags<span class=\"token operator\">:</span> opts<span class=\"token punctuation\">.</span>chromeFlags<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  opts<span class=\"token punctuation\">.</span>port <span class=\"token operator\">=</span> chrome<span class=\"token punctuation\">.</span>port<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lhr <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">lighthouse</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> chrome<span class=\"token punctuation\">.</span><span class=\"token function\">kill</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> lhr<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> opts <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Usage:</span>\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> results <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://izifortune.github.io/lighthouse-custom-gatherer'</span><span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>results<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>If we now try to run our file we should be able to see the results coming from a lighthouse scan in the console:</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">node scan.js</code></pre></div>\n<p>Now that we have a project with Lighthouse up and running we can start looking at how a Gatherer works and how we can use it in our project. We will use a webpage that I’ve created for this <a href=\"https://izifortune.github.io/lighthouse-custom-gatherer\">demo</a>. In the page, I’m fetching todo list items from an API and rendering on the page. I’m measuring the action using <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/Performance\">PerformanceAPI</a> as follows:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token function-variable function\">getDataFromServer</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  performance<span class=\"token punctuation\">.</span><span class=\"token function\">mark</span><span class=\"token punctuation\">(</span><span class=\"token string\">'start'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> todos <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">getTodos</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token function\">renderTodos</span><span class=\"token punctuation\">(</span>todos<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  performance<span class=\"token punctuation\">.</span><span class=\"token function\">mark</span><span class=\"token punctuation\">(</span><span class=\"token string\">'end'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  performance<span class=\"token punctuation\">.</span><span class=\"token function\">measure</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Render todos'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'start'</span><span class=\"token punctuation\">,</span> <span class=\"token string\">'end'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> measure <span class=\"token operator\">=</span> performance<span class=\"token punctuation\">.</span><span class=\"token function\">getEntriesByName</span><span class=\"token punctuation\">(</span><span class=\"token string\">'Render todos'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<h3>Gatherer</h3>\n<p>A Gatherer is used by Lighthouse to collect data on the page. In fact, any data that is currently needed to perform the default lighthouse audits is collected through a Gatherer. We can extend the Gatherer base class and start creating custom ones:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Gatherer <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyGatherer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Gatherer</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token operator\">...</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>The class Gatherer defines three different lifecycle hooks that we can implement in our class:</p>\n<ul>\n<li><strong><em>beforePass</em></strong> - called before the navigation to given URL</li>\n<li><strong><em>pass</em></strong> - called after the page is loaded and the trace is being recorded</li>\n<li><strong><em>afterPass</em></strong> - called after the page is loaded, all the other pass have been executed and a trace is available</li>\n</ul>\n<p>A lifecycle hook is expected to return either directly an Artifact or a Promise which resolve to the desired Artifact. Depending on what data are we looking to collect from the Driver and at what time we can use any of the hooks just described.</p>\n<p>Let’s now create a custom Gatherer which will collect the measurements from the <em>PerformanceAPI</em>. The Gatherer needs then to collect entryType <code class=\"language-text\">measure</code> using a PerformanceObserver. We will proceed to create the file <code class=\"language-text\">todos-gatherer.js</code></p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token string\">'use strict'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Gatherer <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">performance</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">return</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">Promise</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">res</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> <span class=\"token function-variable function\">logger</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token parameter\">list</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n      <span class=\"token keyword\">const</span> entries <span class=\"token operator\">=</span> list<span class=\"token punctuation\">.</span><span class=\"token function\">getEntries</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n      window<span class=\"token punctuation\">.</span>todosPerformance <span class=\"token operator\">=</span> entries<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>duration\n      <span class=\"token function\">res</span><span class=\"token punctuation\">(</span>entries<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>duration<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">let</span> observer <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">PerformanceObserver</span><span class=\"token punctuation\">(</span>logger<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    observer<span class=\"token punctuation\">.</span><span class=\"token function\">observe</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span> entryTypes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'measure'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span> buffered<span class=\"token operator\">:</span> <span class=\"token boolean\">true</span> <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TodosGatherer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Gatherer</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token function\">beforePass</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">options</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> driver <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>driver<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> driver<span class=\"token punctuation\">.</span><span class=\"token function\">evaluateScriptOnNewDocument</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>performance<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)()</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token function\">afterPass</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">options</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> driver <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>driver<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> driver<span class=\"token punctuation\">.</span><span class=\"token function\">evaluateAsync</span><span class=\"token punctuation\">(</span><span class=\"token string\">'window.todosPerformance'</span><span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> TodosGatherer<span class=\"token punctuation\">;</span></code></pre></div>\n<p>Inside TodosGatherer we are using both the <em>beforePass</em> and <em>afterPass</em> hook to contact the Driver and then execute a javascript function inside the context of the current page returning a promise. Inside the <em>beforePass</em> we are registering a <em>PerformanceObserver</em> just after the page will load, since the observers are not buffered we might encounter in a race condition. In the <em>afterPass</em> then we collect the previously registered measure.\nTo get an idea of all the methods that you use on the driver object you can have a look\n<a href=\"https://github.com/GoogleChrome/lighthouse/blob/34a542a156b271fd7725525b09a92a344d7809c0/lighthouse-core/gather/driver.js\">here</a>.</p>\n<p>Now we need to include it in our <code class=\"language-text\">scan.js</code> file:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> lighthouse <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> chromeLauncher <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'chrome-launcher'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> config <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  passes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n    passName<span class=\"token operator\">:</span> <span class=\"token string\">'defaultPass'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">//Needed to run custom Gatherers/Audits in the same pass</span>\n    gatherers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">todos-gatherer</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n<span class=\"token operator\">...</span></code></pre></div>\n<p>If we try to run <code class=\"language-text\">scan.js</code> at this moment we will receive an error that there are no audits to run. A Gatherer on its own doesn’t provide any information but rather output Artifacts used on the Audits to define metrics. To proceed then we will have a look at the Audits then.</p>\n<h3>Audit</h3>\n<p>An Audit defines a metric or score, it takes the Artifacts as an input and calculates the desired score. The different audits that <em>Lighthouse</em> is performing such as <em>FirstMeaningfulPaint</em> or <em>SpeedIndex</em> are all in fact defined as an audit internally.\nTo create a custom <em>Audit</em>, similar to a <em>Gatherer</em>, we will extend a base class <code class=\"language-text\">Audit</code> and implements the basic methods:</p>\n<p>To create a custom <em>Audit</em>, similar to a <em>Gatherer</em>, we will extend a\nbase class <code class=\"language-text\">Audit</code> and implements the basic methods:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Audit <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">MyAudit</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Audit</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">get</span> <span class=\"token function\">meta</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token punctuation\">.</span><span class=\"token punctuation\">.</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">static</span> <span class=\"token function\">audit</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">artifacts</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token operator\">...</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>The class <em>Audit</em> defines two methods that need to be overridden:</p>\n<ul>\n<li><strong><em>meta</em></strong> - used to define information about the audit</li>\n<li><strong><em>audit</em></strong> - takes as input the <em>Artifacts</em> from Gatherers and return a Product metric.</li>\n</ul>\n<p>With this information in mind, we can now implement our custom Audit and start collecting the performance of the todo list. The name of the custom audit file will be <code class=\"language-text\">todos-audit.js</code> and will contain:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token string\">'use strict'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> Audit <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span>Audit<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">TodosAudit</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Audit</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">static</span> <span class=\"token keyword\">get</span> <span class=\"token function\">meta</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      id<span class=\"token operator\">:</span> <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span>\n      title<span class=\"token operator\">:</span> <span class=\"token string\">'Todos are loaded and rendered'</span><span class=\"token punctuation\">,</span>\n      scoreDisplayMode<span class=\"token operator\">:</span> Audit<span class=\"token punctuation\">.</span><span class=\"token constant\">SCORING_MODES</span><span class=\"token punctuation\">.</span><span class=\"token constant\">NUMERIC</span><span class=\"token punctuation\">,</span>\n      failureTitle<span class=\"token operator\">:</span> <span class=\"token string\">'Todos loading is too slow.'</span><span class=\"token punctuation\">,</span>\n      description<span class=\"token operator\">:</span> <span class=\"token string\">'Used to measure time for fetching and rendering todos list'</span><span class=\"token punctuation\">,</span>\n      requiredArtifacts<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">'TodosGatherer'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n\n  <span class=\"token keyword\">static</span> <span class=\"token function\">audit</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">artifacts</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> measure <span class=\"token operator\">=</span> artifacts<span class=\"token punctuation\">.</span>TodosGatherer<span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span>\n      rawValue<span class=\"token operator\">:</span> measure<span class=\"token punctuation\">,</span>\n      score<span class=\"token operator\">:</span> Math<span class=\"token punctuation\">.</span><span class=\"token function\">max</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span> <span class=\"token operator\">-</span> <span class=\"token punctuation\">(</span>measure <span class=\"token operator\">/</span> <span class=\"token number\">1500</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span>\n      displayValue<span class=\"token operator\">:</span> <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">Todos rendering is: </span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>measure<span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">ms</span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> TodosAudit<span class=\"token punctuation\">;</span></code></pre></div>\n<p>Inside the method <code class=\"language-text\">meta</code> we are defining information describing the <em>Audit</em> itself such as id, title, scoreDisplayMode and description. Also, we are configuring the <em>Artifacts</em> which are needed by the <em>Audit</em> in this case <code class=\"language-text\">TodosGatherer</code> is the name of the Gatherer of interest.</p>\n<p>And now we need to add it in the configuration inside <code class=\"language-text\">scan.js</code> similar to what we did previously to the <em>Gatherer</em>.</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> config <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  passes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n    passName<span class=\"token operator\">:</span> 'defaultPass’<span class=\"token punctuation\">,</span>\n    gatherers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">todos-gatherer</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  audits<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  categories<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    todos<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      title<span class=\"token operator\">:</span> <span class=\"token string\">'Todos metrics'</span><span class=\"token punctuation\">,</span>\n      description<span class=\"token operator\">:</span> <span class=\"token string\">'Performance metrics for todos'</span><span class=\"token punctuation\">,</span>\n      auditRefs<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token comment\">// When we add more custom audits, `weight` controls how they're averaged together.</span>\n      <span class=\"token punctuation\">{</span>id<span class=\"token operator\">:</span> <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span> weight<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>Launching now our scan we can notice our custom audit being logged into the console.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/592d2/console.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 33.78378378378378%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsSAAALEgHS3X78AAAA7ElEQVQY022PWW7CQBBEfY5gZvXsi5nFiwwTcf9TpQ3ERCLSU6vro6uqO2VdXaoJ2gbF5YAoOyHS453ncia0x/Q5D0AC3QlhLmWqZSw+zz5VZ4MWWkgjlJVMcMQYZgxMMXsDElHagTdmPE9LLiXEoJy0Ue3AGrV2UhgBE4wA45WyIBVYD0p0kA42dVnLPE1TSTl5r0qxQoovhI/aPXm3PdiTCR+21tbrLdeast+2y7LGXByUByDQjcaNGorQgf+1+D2+tXa/Q368pDzN0rqeEPx4bH/v9ST7J5kOYmvfcBNTLvMSUwK780fJT34A6EY+cOUbIS0AAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Console output\"\n        title=\"Console output\"\n        src=\"/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/102e5/console.png\"\n        srcset=\"/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/83276/console.png 148w,\n/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/ba02f/console.png 295w,\n/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/102e5/console.png 590w,\n/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/74549/console.png 885w,\n/dist/static/d2c34e1d10f26b1b25bb3a88e1209bd4/592d2/console.png 933w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>If you prefer to have the report in a different format such as HTML you can add the output option to the lighthouse function. The options object will be then used by <em>Lighthouse</em> to configure the running audits output format. To recap the final <code class=\"language-text\">scan.js</code> will look like:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> lighthouse <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> chromeLauncher <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'chrome-launcher'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> promisify <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'util'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> writeFile <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fs'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> pWriteFile <span class=\"token operator\">=</span> <span class=\"token function\">promisify</span><span class=\"token punctuation\">(</span>writeFile<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> config <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  passes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n    passName<span class=\"token operator\">:</span> 'defaultPass’<span class=\"token punctuation\">,</span>\n    gatherers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">todos-gatherer</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  audits<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  categories<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    todos<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      title<span class=\"token operator\">:</span> <span class=\"token string\">'Todos metrics'</span><span class=\"token punctuation\">,</span>\n      description<span class=\"token operator\">:</span> <span class=\"token string\">'Performance metrics for todos'</span><span class=\"token punctuation\">,</span>\n      auditRefs<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token comment\">// When we add more custom audits, `weight` controls how they're averaged together.</span>\n      <span class=\"token punctuation\">{</span>id<span class=\"token operator\">:</span> <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span> weight<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> chrome <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> chromeLauncher<span class=\"token punctuation\">.</span><span class=\"token function\">launch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>chromeFlags<span class=\"token operator\">:</span> opts<span class=\"token punctuation\">.</span>chromeFlags<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  opts<span class=\"token punctuation\">.</span>port <span class=\"token operator\">=</span> chrome<span class=\"token punctuation\">.</span>port<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lhr<span class=\"token punctuation\">,</span> report <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">lighthouse</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> chrome<span class=\"token punctuation\">.</span><span class=\"token function\">kill</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> report<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> opts <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  output<span class=\"token operator\">:</span> <span class=\"token string\">'html'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Usage:</span>\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> results <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://izifortune.github.io/lighthouse-custom-gatherer'</span><span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">pWriteFile</span><span class=\"token punctuation\">(</span><span class=\"token string\">'report.html'</span><span class=\"token punctuation\">,</span> results<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>And running it now we will have an HTML report inside <code class=\"language-text\">report.html</code> which will look similar to the following one:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/0985fae75edaa1babaaafd263db33ead/592d2/report1.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 15.54054054054054%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAYElEQVQI142LSw6AIAwFuf8VPQBUAlQ+idBWrNG4NE7ykreYMXPOlJJzzns/xpg/EOrSUI+5YwAIIZRSiOgzPHSElmB5YhFBRI1jjLXWS3nRT53zyhtIS5yB0XLxMnbVTtWgr2l3vqfYAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Report Todo\"\n        title=\"Report Todo\"\n        src=\"/dist/static/0985fae75edaa1babaaafd263db33ead/102e5/report1.png\"\n        srcset=\"/dist/static/0985fae75edaa1babaaafd263db33ead/83276/report1.png 148w,\n/dist/static/0985fae75edaa1babaaafd263db33ead/ba02f/report1.png 295w,\n/dist/static/0985fae75edaa1babaaafd263db33ead/102e5/report1.png 590w,\n/dist/static/0985fae75edaa1babaaafd263db33ead/74549/report1.png 885w,\n/dist/static/0985fae75edaa1babaaafd263db33ead/592d2/report1.png 933w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>We can include also the standards <em>Lighthouse</em> audits together with our custom one by adding to the configuration object the following key:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> config <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">extends</span><span class=\"token operator\">:</span> <span class=\"token string\">'lighthouse:default'</span><span class=\"token punctuation\">,</span> <span class=\"token comment\">// Include Lighthouse default audits</span>\n  passes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n    passName<span class=\"token operator\">:</span> 'defaultPass’<span class=\"token punctuation\">,</span>\n    gatherers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">todos-gatherer</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n<span class=\"token operator\">...</span></code></pre></div>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/26f46/full-report.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 22.972972972972975%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAAz0lEQVQY04WO3VLEIAxG+/7PprMX6s0649ay/YcSSktLQsBUH8BvcnE4kJAKwqqWntKVc51xB84FdtfDJEa8wHpsuZR4bCcMiTki+m1DxEpBd2s/Us4FD1I3tqqU8jDqfbgTp4POl+atd4NIGu/YvAqkxNPiIqVqNvOXehCSTOrqz0VPct2Pff2sMUbh59ghkYAe2qn9ll3AQd00WuuKmTPnvySWYnn3Ky8rXPJ1JKKIFGJk+SWe1pgjhCr/F2kPIVhrffAzmNM770z02w7wAxt7H0YX4dq6AAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lighthouse default + custom report\"\n        title=\"Lighthouse default + custom report\"\n        src=\"/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/102e5/full-report.png\"\n        srcset=\"/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/83276/full-report.png 148w,\n/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/ba02f/full-report.png 295w,\n/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/102e5/full-report.png 590w,\n/dist/static/d4fb59b44bf6225d2f83749a6dddd4fe/26f46/full-report.png 863w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>After the introduction to the Lighthouse architecture, we can start customising the Lighthouse audits by measuring and reporting metrics which are relevant to us. We will explore now how we can use the Gatherers to overcome a common problem while performing the scans on a CI environment.</p>\n<h2>Session guard pages</h2>\n<p>In <em>Ryanair</em>, we are using <em>Lighthouse</em> extensively to audit our webpages as part of an automated job performing scans at regular intervals and then we analyse the results of the scans on a regular basis. One of the main problems that we encountered when you are running an automated scan is how to perform audits on pages behind an authentication or user session. While with manual scans we can easily generate a session before starting the audit; if we are running <em>Lighthouse</em> from a CI environment we will need to generate a session programmatically and pass the information to <em>Lighthouse.</em></p>\n<p>A common approach for user session authentications management for a web application is to generate tokens, often JWT, on the server after a successful login and store the result token in the browser. You can store the token in different storage available in the browser such as <em>LocalStorage, SessionStorage, Cookies.</em> I will not judge here where is the best place to store a token what is interesting to us is how we can write to any of that browser storage so that <em>Lighthouse</em> can access the token and perform an audit.</p>\n<p>By using a custom <em>Gatherer</em> we can create a user session by leveraging the lifecycle hook <em>beforePass</em> which triggers before the navigation to the page URL. In the hook, we call an API to generate a session and then through one of the <em>Driver</em> methods <code class=\"language-text\">evaluateScriptOnNewDocument</code> we can pass any function to be executed in the browser instance.</p>\n<p>For the purpose of this demo, I’ve created another <a href=\"https://izifortune.github.io/lighthouse-custom-gatherer/auth\">page</a> where I’m basically rendering the todos only if the user is authenticated. To fake the authentication I’m checking that a specific token is present in <em>LocalStorage</em> and then start fetching and rendering todos.</p>\n<p>Let’s create a new <em>Gatherer</em> called <em>SessionGatherer</em> in the file\n<code class=\"language-text\">session-gatherer.js</code></p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> Gatherer <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> <span class=\"token constant\">TOKEN</span> <span class=\"token operator\">=</span> <span class=\"token string\">'iOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImp0aSI6IjkzZDU0MDBiLTQ5MzgtNDNmZS1iMjY4LTY2MDJlNDIxMjFiYiIsImlhdCI6MTU1MjkwNjc0NywiZXhwIjoxNTUyOTEwMzQ3fQ.qEJflkN2ntXrQFalBkkw4duCh55HdNBLGXZOV-dS3KQ'</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">function</span> <span class=\"token function\">createSession</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">token</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  localStorage<span class=\"token punctuation\">.</span><span class=\"token function\">setItem</span><span class=\"token punctuation\">(</span><span class=\"token string\">'token'</span><span class=\"token punctuation\">,</span> token<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">SessionGatherer</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">Gatherer</span> <span class=\"token punctuation\">{</span>\n\n  <span class=\"token keyword\">async</span> <span class=\"token function\">beforePass</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">options</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> driver <span class=\"token operator\">=</span> options<span class=\"token punctuation\">.</span>driver<span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">return</span> driver<span class=\"token punctuation\">.</span><span class=\"token function\">evaluateScriptOnNewDocument</span><span class=\"token punctuation\">(</span><span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">(</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span>createSession<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">)('</span><span class=\"token interpolation\"><span class=\"token interpolation-punctuation punctuation\">${</span><span class=\"token constant\">TOKEN</span><span class=\"token interpolation-punctuation punctuation\">}</span></span><span class=\"token string\">')</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span>\n\nmodule<span class=\"token punctuation\">.</span>exports <span class=\"token operator\">=</span> SessionGatherer<span class=\"token punctuation\">;</span></code></pre></div>\n<p>Once we have created the gatherer we need to tell <em>Lighthouse</em> to include it in the list so that it will be running alongside all the other gatherers while performing an audit. We need to will create another file <code class=\"language-text\">scan-auth.js</code> as following:</p>\n<div class=\"gatsby-highlight\" data-language=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token keyword\">const</span> lighthouse <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'lighthouse'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> chromeLauncher <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'chrome-launcher'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> promisify <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'util'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> writeFile <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token function\">require</span><span class=\"token punctuation\">(</span><span class=\"token string\">'fs'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">const</span> pWriteFile <span class=\"token operator\">=</span> <span class=\"token function\">promisify</span><span class=\"token punctuation\">(</span>writeFile<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">const</span> config <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">extends</span><span class=\"token operator\">:</span> <span class=\"token string\">'lighthouse:default'</span><span class=\"token punctuation\">,</span>\n  passes<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n    passName<span class=\"token operator\">:</span> <span class=\"token string\">'defaultPass'</span><span class=\"token punctuation\">,</span>\n    gatherers<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">session-gatherer</span><span class=\"token template-punctuation string\">`</span></span><span class=\"token punctuation\">,</span>\n      <span class=\"token template-string\"><span class=\"token template-punctuation string\">`</span><span class=\"token string\">todos-gatherer</span><span class=\"token template-punctuation string\">`</span></span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  audits<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n    <span class=\"token string\">'todos-audit'</span>\n  <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n  categories<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n    todos<span class=\"token operator\">:</span> <span class=\"token punctuation\">{</span>\n      title<span class=\"token operator\">:</span> <span class=\"token string\">'Todos metrics'</span><span class=\"token punctuation\">,</span>\n      description<span class=\"token operator\">:</span> <span class=\"token string\">'Performance metrics for todos'</span><span class=\"token punctuation\">,</span>\n      auditRefs<span class=\"token operator\">:</span> <span class=\"token punctuation\">[</span>\n      <span class=\"token comment\">// When we add more custom audits, `weight` controls how they're averaged together.</span>\n      <span class=\"token punctuation\">{</span>id<span class=\"token operator\">:</span> <span class=\"token string\">'todos-audit'</span><span class=\"token punctuation\">,</span> weight<span class=\"token operator\">:</span> <span class=\"token number\">1</span><span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n    <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n  <span class=\"token punctuation\">}</span><span class=\"token punctuation\">,</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">async</span> <span class=\"token keyword\">function</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config <span class=\"token operator\">=</span> <span class=\"token keyword\">null</span></span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">const</span> chrome <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> chromeLauncher<span class=\"token punctuation\">.</span><span class=\"token function\">launch</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>chromeFlags<span class=\"token operator\">:</span> opts<span class=\"token punctuation\">.</span>chromeFlags<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  opts<span class=\"token punctuation\">.</span>port <span class=\"token operator\">=</span> chrome<span class=\"token punctuation\">.</span>port<span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">const</span> <span class=\"token punctuation\">{</span> lhr<span class=\"token punctuation\">,</span> report <span class=\"token punctuation\">}</span> <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">lighthouse</span><span class=\"token punctuation\">(</span>url<span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token keyword\">await</span> chrome<span class=\"token punctuation\">.</span><span class=\"token function\">kill</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n  <span class=\"token keyword\">return</span> report<span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token keyword\">const</span> opts <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n  output<span class=\"token operator\">:</span> <span class=\"token string\">'html'</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n\n<span class=\"token comment\">// Usage:</span>\n<span class=\"token punctuation\">(</span><span class=\"token keyword\">async</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token operator\">=></span> <span class=\"token punctuation\">{</span>\n  <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">const</span> results <span class=\"token operator\">=</span> <span class=\"token keyword\">await</span> <span class=\"token function\">launchChromeAndRunLighthouse</span><span class=\"token punctuation\">(</span><span class=\"token string\">'https://izifortune.github.io/lighthouse-custom-gatherer/auth'</span><span class=\"token punctuation\">,</span> opts<span class=\"token punctuation\">,</span> config<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">await</span> <span class=\"token function\">pWriteFile</span><span class=\"token punctuation\">(</span><span class=\"token string\">'report.html'</span><span class=\"token punctuation\">,</span> results<span class=\"token punctuation\">)</span>\n  <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    console<span class=\"token punctuation\">.</span><span class=\"token function\">log</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n  <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>Now we can start running our scans on the pages behind a user session and monitor their performance.</p>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">node scan-auth.js</code></pre></div>\n<p>Which record the default <em>Lighthouse</em> metrics plus the custom Todos metrics which on this case are behind a user authentication.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/a62c5f39283203a6976b8db10d40eb12/a0a4d/full-report-auth.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 22.972972972972975%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsSAAALEgHS3X78AAAA1ElEQVQY0z2P3U7DMAyF8/6PNqFdAHcMsbQL7ZqsIW3+Y8e4IDg6so4+yzqJGNfJ7GsnqjlmO7dWAXvxtnqLnVzaZ2caHMpO1+AAeoiRzUScxuc3cyUisGOTJ4JCiE29oB0Yvht5Hl8LAtXU5BN+KYY+5XXzHIRUw2I0J2cfSl5KrTHGefww+s4wpHh/LBwYLrersxYQP6dpuKkQgvh5ERzNgLVB7x25GdgHZDFp/JlaQ2YVbGVzzmrNUPyu+YAn/Qc6jH/y3nOPCxvPvNsUQ3FbSukbUyAfo+wqrRsAAAAASUVORK5CYII='); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Lighthouse default + custom report behind auth\"\n        title=\"Lighthouse default + custom report behind auth\"\n        src=\"/dist/static/a62c5f39283203a6976b8db10d40eb12/102e5/full-report-auth.png\"\n        srcset=\"/dist/static/a62c5f39283203a6976b8db10d40eb12/83276/full-report-auth.png 148w,\n/dist/static/a62c5f39283203a6976b8db10d40eb12/ba02f/full-report-auth.png 295w,\n/dist/static/a62c5f39283203a6976b8db10d40eb12/102e5/full-report-auth.png 590w,\n/dist/static/a62c5f39283203a6976b8db10d40eb12/74549/full-report-auth.png 885w,\n/dist/static/a62c5f39283203a6976b8db10d40eb12/7213b/full-report-auth.png 1180w,\n/dist/static/a62c5f39283203a6976b8db10d40eb12/a0a4d/full-report-auth.png 1726w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>Inside the report now if you look at the <em>filmstrip</em> you will notice that the todo list got rendered correctly meaning that a valid token was found in <em>LocalStorage</em>.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto;  max-width: 590px;\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/dist/static/803c12777c24c6ed0ae0945e51523095/a0a4d/filmstrip.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 14.864864864864865%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAIAAAAcOLh5AAAACXBIWXMAAAsSAAALEgHS3X78AAAAcklEQVQI102NSQrFIBBEc/9LKiLOaJx1o79+hJBaND286rrWWnvvU3POtdaUUmstxth7v+97jIGKTX4E4JzAX1+ztVYIIaVUSnHOnXOU0hACIcR7zxjTWuMKxhjzN++PENIe4TESMJZS5pwIPD32LwD+B14Hq5daX4MgAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <img\n        class=\"gatsby-resp-image-image\"\n        alt=\"Filmstrip\"\n        title=\"Filmstrip\"\n        src=\"/dist/static/803c12777c24c6ed0ae0945e51523095/102e5/filmstrip.png\"\n        srcset=\"/dist/static/803c12777c24c6ed0ae0945e51523095/83276/filmstrip.png 148w,\n/dist/static/803c12777c24c6ed0ae0945e51523095/ba02f/filmstrip.png 295w,\n/dist/static/803c12777c24c6ed0ae0945e51523095/102e5/filmstrip.png 590w,\n/dist/static/803c12777c24c6ed0ae0945e51523095/74549/filmstrip.png 885w,\n/dist/static/803c12777c24c6ed0ae0945e51523095/7213b/filmstrip.png 1180w,\n/dist/static/803c12777c24c6ed0ae0945e51523095/a0a4d/filmstrip.png 1726w\"\n        sizes=\"(max-width: 590px) 100vw, 590px\"\n        style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n        loading=\"lazy\"\n      />\n  </a>\n    </span></p>\n<p>I’ve collected the examples presented here in this repository <a href=\"https://github.com/izifortune/lighthouse-custom-gatherer\">https://github.com/izifortune/lighthouse-custom-gatherer</a></p>\n<h2>Credits</h2>\n<p>The initial idea came by looking at the custom audit recipe from the <em>Lighthouse</em> team that you can find <a href=\"https://github.com/GoogleChrome/lighthouse/tree/master/docs/recipes/custom-audit\">here</a>.\nThe recipe served as an inspiration and a first example to create a custom Gatherer/Audit. I also would like to thanks @patrickhulce for his time and prompt answers on Gitter.</p>","frontmatter":{"title":"Lighthouse architecture demystified","date":"March 20, 2019","description":"Practical introduction to Google Lighthouse architecture and building blocks to start collecting custom metrics and run audits behind a user session.","socialImage":{"childImageSharp":{"fixed":{"src":"/dist/static/fc44be6bd302a3ddeb4b1faa997ae438/f6363/lighthouse.jpg"}}},"image":{"childImageSharp":{"fluid":{"base64":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAIAAAC9o5sfAAAACXBIWXMAAAsSAAALEgHS3X78AAABdElEQVQoz2NQSdwNR0oJe+Ti9srH7VWI26sUv1spaZds4n4gqZK0UyVxDxjtRkYMEEo1cbdi/B6XkrXRjUu8ylfbF601TNlvHnxiTnCsk/9WuYjTqok7VBJ34dCctEsudl9y24LpS7t6FvR2TlhcUXWgs2zt/4UKRW5rxc3/qyS8Vk46jKYfxWb7wg3BdcucIq5a27/XMf/i4fb8QOOkYMuTChb/Vaz+K3s+UEnegex4BmSTFJN2ygeeV9H5r2j+Vzzsv4v/wxNZ5RZ2L1UM/qnp/FY1+KUSeUwleSc2zQl7VFO2q7k+VNf9q+n0U9Hvv1n8j/7yi252X5U1/2sY/1bX/6fsdUsldRtQJabm3SBX2bxR0/6vpvdHy/VOQN2Crr5oy/TZ8r431E2/q2n9V3V5iENzIliz520V1weqUYcdFie6bfDWaumSnl4htzBLKW2bivsdFf/LKik7cGiGGwGM2/i9SjFHVKOOqkUfArIR4kgqAX/s6txUvscLAAAAAElFTkSuQmCC","aspectRatio":2.352112676056338,"src":"/dist/static/9a37d9716f7a0500d32f04301023aaa0/a5924/lighthouse2.png","srcSet":"/dist/static/9a37d9716f7a0500d32f04301023aaa0/17cc0/lighthouse2.png 167w,\n/dist/static/9a37d9716f7a0500d32f04301023aaa0/f7869/lighthouse2.png 333w,\n/dist/static/9a37d9716f7a0500d32f04301023aaa0/a5924/lighthouse2.png 666w,\n/dist/static/9a37d9716f7a0500d32f04301023aaa0/4f774/lighthouse2.png 674w","sizes":"(max-width: 666px) 100vw, 666px"}}}}}},"pageContext":{"slug":"/lighthouse-architecture-demystified/","previous":{"fields":{"slug":"/angular-subresource-integrity/"},"frontmatter":{"title":"Angular subresource integrity"}},"next":{"fields":{"slug":"/serverless-website-sam-aws/"},"frontmatter":{"title":"Build a serverless website with SAM on AWS"}}}}}