lighthouse 12.0.0-dev.20240529 → 12.0.0-dev.20240531

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.
@@ -4,12 +4,15 @@ export type PreparedAssets = {
4
4
  };
5
5
  /**
6
6
  * Save artifacts object mostly to single file located at basePath/artifacts.json.
7
- * Also save the traces & devtoolsLogs to their own files
7
+ * Also save the traces & devtoolsLogs to their own files, with optional compression.
8
8
  * @param {LH.Artifacts} artifacts
9
9
  * @param {string} basePath
10
+ * @param {{gzip?: boolean}} options
10
11
  * @return {Promise<void>}
11
12
  */
12
- export function saveArtifacts(artifacts: LH.Artifacts, basePath: string): Promise<void>;
13
+ export function saveArtifacts(artifacts: LH.Artifacts, basePath: string, options?: {
14
+ gzip?: boolean;
15
+ }): Promise<void>;
13
16
  /**
14
17
  * Saves flow artifacts with the following file structure:
15
18
  * flow/ -- Directory specified by `basePath`.
@@ -32,11 +35,6 @@ export function saveFlowArtifacts(flowArtifacts: LH.UserFlow.FlowArtifacts, base
32
35
  * @param {string} basePath
33
36
  */
34
37
  export function saveLhr(lhr: LH.Result, basePath: string): void;
35
- /**
36
- * @typedef {object} PreparedAssets
37
- * @property {LH.Trace} [traceData]
38
- * @property {LH.DevtoolsLog} [devtoolsLog]
39
- */
40
38
  /**
41
39
  * Load artifacts object from files located within basePath
42
40
  * Also save the traces to their own files
@@ -67,16 +65,22 @@ export function prepareAssets(artifacts: LH.Artifacts, audits?: Record<string, i
67
65
  * Save a trace as JSON by streaming to disk at traceFilename.
68
66
  * @param {LH.Trace} traceData
69
67
  * @param {string} traceFilename
68
+ * @param {{gzip?: boolean}=} options
70
69
  * @return {Promise<void>}
71
70
  */
72
- export function saveTrace(traceData: LH.Trace, traceFilename: string): Promise<void>;
71
+ export function saveTrace(traceData: LH.Trace, traceFilename: string, options?: {
72
+ gzip?: boolean;
73
+ } | undefined): Promise<void>;
73
74
  /**
74
75
  * Save a devtoolsLog as JSON by streaming to disk at devtoolLogFilename.
75
76
  * @param {LH.DevtoolsLog} devtoolsLog
76
77
  * @param {string} devtoolLogFilename
78
+ * @param {{gzip?: boolean}=} options
77
79
  * @return {Promise<void>}
78
80
  */
79
- export function saveDevtoolsLog(devtoolsLog: import("../index.js").DevtoolsLog, devtoolLogFilename: string): Promise<void>;
81
+ export function saveDevtoolsLog(devtoolsLog: import("../index.js").DevtoolsLog, devtoolLogFilename: string, options?: {
82
+ gzip?: boolean;
83
+ } | undefined): Promise<void>;
80
84
  /**
81
85
  * @param {LH.DevtoolsLog} devtoolsLog
82
86
  * @param {string} outputPath
@@ -8,6 +8,7 @@ import fs from 'fs';
8
8
  import path from 'path';
9
9
  import stream from 'stream';
10
10
  import url from 'url';
11
+ import {createGzip, gunzipSync} from 'zlib';
11
12
 
12
13
  import log from 'lighthouse-logger';
13
14
 
@@ -33,6 +34,46 @@ const stepDirectoryRegex = /^step(\d+)$/;
33
34
  * @property {LH.DevtoolsLog} [devtoolsLog]
34
35
  */
35
36
 
37
+ /**
38
+ * @param {import('stream').PipelineSource<any>} contents
39
+ * @param {string} path
40
+ * @param {boolean} gzip
41
+ */
42
+ async function writeJson(contents, path, gzip) {
43
+ const writeStream = fs.createWriteStream(gzip ? path + '.gz' : path);
44
+ if (gzip) {
45
+ await stream.promises.pipeline(contents, createGzip(), writeStream);
46
+ } else {
47
+ await stream.promises.pipeline(contents, writeStream);
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Prefers reading a gzipped file (.gz) if present.
53
+ * @param {string} filename
54
+ * @param {(this: any, key: string, value: any) => any=} reviver
55
+ */
56
+ function readJson(filename, reviver) {
57
+ if (fs.existsSync(filename + '.gz')) {
58
+ filename = filename + '.gz';
59
+ }
60
+
61
+ if (!filename.endsWith('.json.gz')) {
62
+ return JSON.parse(fs.readFileSync(filename, 'utf8'), reviver);
63
+ }
64
+
65
+ const buffer = gunzipSync(fs.readFileSync(filename));
66
+ return JSON.parse(buffer.toString('utf8'), reviver);
67
+ }
68
+
69
+ /**
70
+ * @param {string} filename
71
+ * @param {string} suffix
72
+ * @returns
73
+ */
74
+ function endsWithSuffix(filename, suffix) {
75
+ return filename.endsWith(suffix) || filename.endsWith(suffix + '.gz');
76
+ }
36
77
 
37
78
  /**
38
79
  * Load artifacts object from files located within basePath
@@ -48,16 +89,15 @@ function loadArtifacts(basePath) {
48
89
  }
49
90
 
50
91
  // load artifacts.json using a reviver to deserialize any LighthouseErrors in artifacts.
51
- const artifactsStr = fs.readFileSync(path.join(basePath, artifactsFilename), 'utf8');
52
92
  /** @type {LH.Artifacts} */
53
- const artifacts = JSON.parse(artifactsStr, LighthouseError.parseReviver);
93
+ const artifacts = readJson(path.join(basePath, artifactsFilename), LighthouseError.parseReviver);
54
94
 
55
95
  const filenames = fs.readdirSync(basePath);
56
96
 
57
- filenames.filter(f => f.endsWith(devtoolsLogSuffix)).forEach(filename => {
97
+ filenames.filter(f => endsWithSuffix(f, devtoolsLogSuffix)).forEach(filename => {
58
98
  if (!artifacts.devtoolsLogs) artifacts.devtoolsLogs = {};
59
- const prefix = filename.replace(devtoolsLogSuffix, '');
60
- const devtoolsLog = JSON.parse(fs.readFileSync(path.join(basePath, filename), 'utf8'));
99
+ const prefix = filename.replace(devtoolsLogSuffix + '.gz', '').replace(devtoolsLogSuffix, '');
100
+ const devtoolsLog = readJson(path.join(basePath, filename));
61
101
  artifacts.devtoolsLogs[prefix] = devtoolsLog;
62
102
  if (prefix === defaultPrefix) {
63
103
  artifacts.DevtoolsLog = devtoolsLog;
@@ -67,11 +107,10 @@ function loadArtifacts(basePath) {
67
107
  }
68
108
  });
69
109
 
70
- filenames.filter(f => f.endsWith(traceSuffix)).forEach(filename => {
110
+ filenames.filter(f => endsWithSuffix(f, traceSuffix)).forEach(filename => {
71
111
  if (!artifacts.traces) artifacts.traces = {};
72
- const file = fs.readFileSync(path.join(basePath, filename), {encoding: 'utf-8'});
73
- const trace = JSON.parse(file);
74
- const prefix = filename.replace(traceSuffix, '');
112
+ const trace = readJson(path.join(basePath, filename));
113
+ const prefix = filename.replace(traceSuffix + '.gz', '').replace(traceSuffix, '');
75
114
  artifacts.traces[prefix] = Array.isArray(trace) ? {traceEvents: trace} : trace;
76
115
  if (prefix === defaultPrefix) {
77
116
  artifacts.Trace = artifacts.traces[prefix];
@@ -200,12 +239,13 @@ async function saveFlowArtifacts(flowArtifacts, basePath) {
200
239
 
201
240
  /**
202
241
  * Save artifacts object mostly to single file located at basePath/artifacts.json.
203
- * Also save the traces & devtoolsLogs to their own files
242
+ * Also save the traces & devtoolsLogs to their own files, with optional compression.
204
243
  * @param {LH.Artifacts} artifacts
205
244
  * @param {string} basePath
245
+ * @param {{gzip?: boolean}} options
206
246
  * @return {Promise<void>}
207
247
  */
208
- async function saveArtifacts(artifacts, basePath) {
248
+ async function saveArtifacts(artifacts, basePath, options = {}) {
209
249
  const status = {msg: 'Saving artifacts', id: 'lh:assetSaver:saveArtifacts'};
210
250
  log.time(status);
211
251
  fs.mkdirSync(basePath, {recursive: true});
@@ -213,8 +253,11 @@ async function saveArtifacts(artifacts, basePath) {
213
253
  // Delete any previous artifacts in this directory.
214
254
  const filenames = fs.readdirSync(basePath);
215
255
  for (const filename of filenames) {
216
- if (filename.endsWith(traceSuffix) || filename.endsWith(devtoolsLogSuffix) ||
217
- filename === artifactsFilename) {
256
+ const isPreviousFile =
257
+ filename.endsWith(traceSuffix) || filename.endsWith(devtoolsLogSuffix) ||
258
+ filename.endsWith(traceSuffix + '.gz') || filename.endsWith(devtoolsLogSuffix + '.gz') ||
259
+ filename === artifactsFilename || filename === artifactsFilename + '.gz';
260
+ if (isPreviousFile) {
218
261
  fs.unlinkSync(`${basePath}/${filename}`);
219
262
  }
220
263
  }
@@ -234,24 +277,30 @@ async function saveArtifacts(artifacts, basePath) {
234
277
  } = artifacts;
235
278
 
236
279
  if (Trace) {
237
- await saveTrace(Trace, `${basePath}/${defaultPrefix}${traceSuffix}`);
280
+ await saveTrace(Trace, `${basePath}/${defaultPrefix}${traceSuffix}`, options);
238
281
  }
239
282
 
240
283
  if (TraceError) {
241
- await saveTrace(TraceError, `${basePath}/${errorPrefix}${traceSuffix}`);
284
+ await saveTrace(TraceError, `${basePath}/${errorPrefix}${traceSuffix}`, options);
242
285
  }
243
286
 
244
287
  if (DevtoolsLog) {
245
- await saveDevtoolsLog(DevtoolsLog, `${basePath}/${defaultPrefix}${devtoolsLogSuffix}`);
288
+ await saveDevtoolsLog(
289
+ DevtoolsLog, `${basePath}/${defaultPrefix}${devtoolsLogSuffix}`, options);
246
290
  }
247
291
 
248
292
  if (DevtoolsLogError) {
249
- await saveDevtoolsLog(DevtoolsLogError, `${basePath}/${errorPrefix}${devtoolsLogSuffix}`);
293
+ await saveDevtoolsLog(
294
+ DevtoolsLogError, `${basePath}/${errorPrefix}${devtoolsLogSuffix}`, options);
250
295
  }
251
296
 
252
297
  // save everything else, using a replacer to serialize LighthouseErrors in the artifacts.
253
- const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2) + '\n';
254
- fs.writeFileSync(`${basePath}/${artifactsFilename}`, restArtifactsString, 'utf8');
298
+ const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2);
299
+ await writeJson(function* () {
300
+ yield restArtifactsString;
301
+ yield '\n';
302
+ }, `${basePath}/${artifactsFilename}`, !!options.gzip);
303
+
255
304
  log.log('Artifacts saved to disk in folder:', basePath);
256
305
  log.timeEnd(status);
257
306
  }
@@ -371,28 +420,26 @@ function* traceJsonGenerator(traceData) {
371
420
  * Save a trace as JSON by streaming to disk at traceFilename.
372
421
  * @param {LH.Trace} traceData
373
422
  * @param {string} traceFilename
423
+ * @param {{gzip?: boolean}=} options
374
424
  * @return {Promise<void>}
375
425
  */
376
- async function saveTrace(traceData, traceFilename) {
426
+ function saveTrace(traceData, traceFilename, options = {}) {
377
427
  const traceIter = traceJsonGenerator(traceData);
378
- const writeStream = fs.createWriteStream(traceFilename);
379
-
380
- return stream.promises.pipeline(traceIter, writeStream);
428
+ return writeJson(traceIter, traceFilename, !!options.gzip);
381
429
  }
382
430
 
383
431
  /**
384
432
  * Save a devtoolsLog as JSON by streaming to disk at devtoolLogFilename.
385
433
  * @param {LH.DevtoolsLog} devtoolsLog
386
434
  * @param {string} devtoolLogFilename
435
+ * @param {{gzip?: boolean}=} options
387
436
  * @return {Promise<void>}
388
437
  */
389
- function saveDevtoolsLog(devtoolsLog, devtoolLogFilename) {
390
- const writeStream = fs.createWriteStream(devtoolLogFilename);
391
-
392
- return stream.promises.pipeline(function* () {
438
+ function saveDevtoolsLog(devtoolsLog, devtoolLogFilename, options = {}) {
439
+ return writeJson(function* () {
393
440
  yield* arrayOfObjectsJsonGenerator(devtoolsLog);
394
441
  yield '\n';
395
- }, writeStream);
442
+ }, devtoolLogFilename, !!options.gzip);
396
443
  }
397
444
 
398
445
  /**
@@ -380,7 +380,7 @@ const UIStrings = {
380
380
  /**
381
381
  * @description Description text for not restored reason NotMainFrame.
382
382
  */
383
- contentWebAuthenticationAPI: 'Pages that use WebAuthetication API are not eligible for back/forward cache.',
383
+ contentWebAuthenticationAPI: 'Pages that use WebAuthentication API are not eligible for back/forward cache.',
384
384
  /**
385
385
  * @description Description text for not restored reason NotMainFrame.
386
386
  */
@@ -674,4 +674,4 @@ const NotRestoredReasonDescription = {
674
674
  export {
675
675
  NotRestoredReasonDescription,
676
676
  UIStrings,
677
- };
677
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "12.0.0-dev.20240529",
4
+ "version": "12.0.0-dev.20240531",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -1740,7 +1740,7 @@
1740
1740
  "message": "Pages that use Serial API are not eligible for back/forward cache."
1741
1741
  },
1742
1742
  "core/lib/bf-cache-strings.js | contentWebAuthenticationAPI": {
1743
- "message": "Pages that use WebAuthetication API are not eligible for back/forward cache."
1743
+ "message": "Pages that use WebAuthentication API are not eligible for back/forward cache."
1744
1744
  },
1745
1745
  "core/lib/bf-cache-strings.js | contentWebBluetooth": {
1746
1746
  "message": "Pages that use WebBluetooth API are not eligible for back/forward cache."
@@ -1740,7 +1740,7 @@
1740
1740
  "message": "P̂áĝéŝ t́ĥát̂ úŝé Ŝér̂íâĺ ÂṔÎ ár̂é n̂ót̂ él̂íĝíb̂ĺê f́ôŕ b̂áĉḱ/f̂ór̂ẃâŕd̂ ćâćĥé."
1741
1741
  },
1742
1742
  "core/lib/bf-cache-strings.js | contentWebAuthenticationAPI": {
1743
- "message": "P̂áĝéŝ t́ĥát̂ úŝé Ŵéb̂Áût́ĥét̂íĉát̂íôṔr̂ól̂íĝíb̂ĺôáḱ/ór̂ẃâŕâćé."
1743
+ "message": "P̂áĝéŝ t́ĥát̂ úŝé Ŵéb̂Áût́ĥét́îćât́îón̂ ÁP̂Í âŕê ńôt́ êĺîb́l̂é f̂ór̂ b́âćk̂/f́ôŕŵár̂d́ ĉáĉh́ê."
1744
1744
  },
1745
1745
  "core/lib/bf-cache-strings.js | contentWebBluetooth": {
1746
1746
  "message": "P̂áĝéŝ t́ĥát̂ úŝé Ŵéb̂B́l̂úêt́ôót̂h́ ÂṔÎ ár̂é n̂ót̂ él̂íĝíb̂ĺê f́ôŕ b̂áĉḱ/f̂ór̂ẃâŕd̂ ćâćĥé."