@@ -6,19 +6,21 @@ const lighthouse = require('lighthouse');
66const printer = require ( 'lighthouse/lighthouse-cli/printer' ) ;
77const logger = require ( 'lighthouse-logger' ) ;
88const puppeteer = require ( 'puppeteer' ) ;
9+ const fs = require ( 'fs' ) ;
10+ const path = require ( 'path' ) ;
11+
12+ const projectDir = path . join ( __dirname , '../' ) ;
13+ const reportsDir = path . join ( projectDir , 'dist/reports' ) ;
914
1015// Constants
11- const AUDIT_CATEGORIES =
12- [ 'accessibility' , 'best-practices' , 'performance' , 'pwa' , 'seo' ] ;
16+ const AUDIT_CATEGORIES = [ 'accessibility' , 'best-practices' , 'performance' , 'pwa' , 'seo' ] ;
1317/**
1418 * @type {LH.Flags }
1519 */
1620const LIGHTHOUSE_FLAGS = {
17- logLevel : process . env . CI ? 'error' : 'info'
21+ logLevel : process . env . CI ? 'error' : 'info' ,
1822} ; // Be less verbose on CI.
19- const SKIPPED_HTTPS_AUDITS = [
20- 'uses-long-cache-ttl' , 'canonical' , 'uses-text-compression'
21- ] ;
23+ const SKIPPED_HTTPS_AUDITS = [ 'uses-long-cache-ttl' , 'canonical' , 'uses-text-compression' ] ;
2224const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer' ;
2325const WAIT_FOR_SW_DELAY = 5000 ;
2426
@@ -62,27 +64,31 @@ _main(process.argv.slice(2)).then(() => console.log('Audit completed.'));
6264 * @private
6365 */
6466async function _main ( args ) {
65- const { url, minScores, logFile } = parseInput ( args ) ;
67+ const { url, minScores} = parseInput ( args ) ;
6668 const isOnHttp = / ^ h t t p : / . test ( url ) ;
6769 /**
6870 * @type {LH.Flags }
6971 */
7072 const lhFlags = {
7173 ...LIGHTHOUSE_FLAGS ,
72- onlyCategories : Object . keys ( minScores ) . sort ( )
74+ onlyCategories : Object . keys ( minScores ) . sort ( ) ,
7375 } ;
7476 /**
7577 * @type {LH.Config.Json }
7678 */
7779 const lhConfig = {
78- extends : 'lighthouse:default' ,
80+ extends : 'lighthouse:default' ,
7981 // Since the Angular ServiceWorker waits for the app to stabilize before
8082 // registering, wait a few seconds after load to allow Lighthouse to
8183 // reliably detect it.
82- passes :
83- [ { passName : 'defaultPass' , pauseAfterLoadMs : WAIT_FOR_SW_DELAY } ] ,
84+ passes : [ { passName : 'defaultPass' , pauseAfterLoadMs : WAIT_FOR_SW_DELAY } ] ,
8485 } ;
8586
87+ await cleanupAndPrepareReportsDir ( ) ;
88+
89+ // Always generate report/log files.
90+ const logFile = path . join ( reportsDir , `${ url . replace ( / \/ / g, '-' ) } --report.json` ) ;
91+
8692 console . log ( `Running web-app audits for '${ url } '...` ) ;
8793 console . log ( ` Audit categories: ${ lhFlags . onlyCategories . join ( ', ' ) } ` ) ;
8894
@@ -102,8 +108,7 @@ async function _main(args) {
102108 onError ( 'Lighthouse failed to return any results.' ) ;
103109 }
104110 const success = await processResults ( results , minScores , logFile ) ;
105- console . log (
106- `\n(Completed in ${ ( ( Date . now ( ) - startTime ) / 1000 ) . toFixed ( 1 ) } s.)\n` ) ;
111+ console . log ( `\n(Completed in ${ ( ( Date . now ( ) - startTime ) / 1000 ) . toFixed ( 1 ) } s.)\n` ) ;
107112
108113 if ( ! success ) {
109114 onError ( 'One or more scores are below the minimum required.' ) ;
@@ -128,8 +133,11 @@ function formatScore(score) {
128133 * @returns {Promise<LH.RunnerResult | undefined> } the result of the Lighthouse run
129134 */
130135async function launchChromeAndRunLighthouse ( url , flags , config ) {
131- const browser = await puppeteer . launch ( ) ;
132- flags . port = ( new URL ( browser . wsEndpoint ( ) ) ) . port ;
136+ const browser = await puppeteer . launch ( {
137+ // Allow for a custom chromium to be provided (e.g. for M1 native support)
138+ executablePath : process . env . CHROMIUM_BIN ,
139+ } ) ;
140+ flags . port = new URL ( browser . wsEndpoint ( ) ) . port ;
133141
134142 try {
135143 return await lighthouse ( url , flags , config ) ;
@@ -147,14 +155,22 @@ function onError(err) {
147155 process . exit ( 1 ) ;
148156}
149157
158+ async function cleanupAndPrepareReportsDir ( ) {
159+ try {
160+ await fs . promises . rm ( reportsDir , { recursive : true } ) ;
161+ } catch { }
162+
163+ await fs . promises . mkdir ( reportsDir , { recursive : true } ) ;
164+ }
165+
150166/**
151167 * Parse CLI args and throw errors if any are invalid.
152168 * @param {string[] } args <url> <min-scores> [<log-file>]
153- * @returns {{url: string, minScores: {all?: number, performance?: number, accessibility?: number, 'best-practices'?: number, pwa?: number, seo?: number}, logFile?: string } }
169+ * @returns {{url: string, minScores: {all?: number, performance?: number, accessibility?: number, 'best-practices'?: number, pwa?: number, seo?: number}} }
154170 * the validated URL, parsed minimum scores, and optional file path to write the report to
155171 */
156172function parseInput ( args ) {
157- const [ url , minScoresRaw , logFile ] = args ;
173+ const [ url , minScoresRaw ] = args ;
158174
159175 if ( ! url ) {
160176 onError ( 'Invalid arguments: <url> not specified.' ) ;
@@ -163,21 +179,22 @@ function parseInput(args) {
163179 }
164180
165181 const minScores = parseMinScores ( minScoresRaw || '' ) ;
166- const unknownCategories =
167- Object . keys ( minScores ) . filter ( cat => ! AUDIT_CATEGORIES . includes ( cat ) ) ;
168- const allValuesValid =
169- Object . values ( minScores ) . every ( x => ( 0 <= x ) && ( x <= 1 ) ) ;
182+ const unknownCategories = Object . keys ( minScores ) . filter ( cat => ! AUDIT_CATEGORIES . includes ( cat ) ) ;
183+ const allValuesValid = Object . values ( minScores ) . every ( x => 0 <= x && x <= 1 ) ;
170184
171185 if ( unknownCategories . length > 0 ) {
172- onError ( `Invalid arguments: <min-scores> contains unknown category(-ies): ${
173- unknownCategories . join ( ', ' ) } `) ;
186+ onError (
187+ `Invalid arguments: <min-scores> contains unknown category(-ies): ${ unknownCategories . join (
188+ ', '
189+ ) } `
190+ ) ;
174191 } else if ( ! allValuesValid ) {
175192 onError (
176- `Invalid arguments: <min-scores> has non-numeric or out-of-range values: ${
177- minScoresRaw } ` ) ;
193+ `Invalid arguments: <min-scores> has non-numeric or out-of-range values: ${ minScoresRaw } `
194+ ) ;
178195 }
179196
180- return { url, minScores, logFile } ;
197+ return { url, minScores} ;
181198}
182199
183200/**
@@ -202,13 +219,15 @@ function parseMinScores(raw) {
202219 raw = `all:${ raw } ` ;
203220 }
204221
205- raw . split ( ',' )
206- . map ( x => x . split ( ':' ) )
207- . forEach ( ( [ key , val ] ) => minScores [ key ] = Number ( val ) / 100 ) ;
222+ raw
223+ . split ( ',' )
224+ . map ( x => x . split ( ':' ) )
225+ . forEach ( ( [ key , val ] ) => ( minScores [ key ] = Number ( val ) / 100 ) ) ;
208226
209227 if ( minScores . hasOwnProperty ( 'all' ) ) {
210- AUDIT_CATEGORIES . forEach ( cat => minScores . hasOwnProperty ( cat ) ||
211- ( minScores [ cat ] = minScores . all ) ) ;
228+ AUDIT_CATEGORIES . forEach (
229+ cat => minScores . hasOwnProperty ( cat ) || ( minScores [ cat ] = minScores . all )
230+ ) ;
212231 delete minScores . all ;
213232 }
214233
@@ -236,19 +255,23 @@ async function processResults(results, minScores, logFile) {
236255 console . log ( `\nLighthouse version: ${ lhVersion } ` ) ;
237256 console . log ( '\nAudit results:' ) ;
238257
239- const maxTitleLen =
240- Math . max ( ...Object . values ( categories ) . map ( ( { title} ) => title . length ) ) ;
241- return Object . keys ( categories ) . sort ( ) . reduce ( ( aggr , cat ) => {
242- const { title, score} = categories [ cat ] ;
243- const paddedTitle = `${ title } :` . padEnd ( maxTitleLen + 1 ) ;
244- const minScore = minScores [ cat ] ;
245- const passed = ! isNaN ( score ) && ( score >= minScore ) ;
246-
247- console . log ( ` - ${ paddedTitle } ${ formatScore ( score ) } (Required: ${
248- formatScore ( minScore ) } ) ${ passed ? 'OK' : 'FAILED' } `) ;
249-
250- return aggr && passed ;
251- } , true ) ;
258+ const maxTitleLen = Math . max ( ...Object . values ( categories ) . map ( ( { title} ) => title . length ) ) ;
259+ return Object . keys ( categories )
260+ . sort ( )
261+ . reduce ( ( aggr , cat ) => {
262+ const { title, score} = categories [ cat ] ;
263+ const paddedTitle = `${ title } :` . padEnd ( maxTitleLen + 1 ) ;
264+ const minScore = minScores [ cat ] ;
265+ const passed = ! isNaN ( score ) && score >= minScore ;
266+
267+ console . log (
268+ ` - ${ paddedTitle } ${ formatScore ( score ) } (Required: ${ formatScore ( minScore ) } ) ${
269+ passed ? 'OK' : 'FAILED'
270+ } `
271+ ) ;
272+
273+ return aggr && passed ;
274+ } , true ) ;
252275}
253276
254277/**
@@ -257,7 +280,6 @@ async function processResults(results, minScores, logFile) {
257280 * @param {LH.Config.Json } config
258281 */
259282function skipHttpsAudits ( config ) {
260- console . log (
261- ` Skipping HTTPS-related audits: ${ SKIPPED_HTTPS_AUDITS . join ( ', ' ) } ` ) ;
262- config . settings = { ...config . settings , skipAudits : SKIPPED_HTTPS_AUDITS } ;
283+ console . log ( ` Skipping HTTPS-related audits: ${ SKIPPED_HTTPS_AUDITS . join ( ', ' ) } ` ) ;
284+ config . settings = { ...config . settings , skipAudits : SKIPPED_HTTPS_AUDITS } ;
263285}
0 commit comments