lighthouse 12.4.0 → 12.5.0

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.
Files changed (164) hide show
  1. package/core/audits/audit.js +3 -1
  2. package/core/audits/bootup-time.js +1 -1
  3. package/core/audits/byte-efficiency/duplicated-javascript.js +1 -1
  4. package/core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  5. package/core/audits/byte-efficiency/legacy-javascript.d.ts +0 -65
  6. package/core/audits/byte-efficiency/legacy-javascript.js +11 -363
  7. package/core/audits/byte-efficiency/modern-image-formats.js +1 -1
  8. package/core/audits/byte-efficiency/offscreen-images.js +4 -3
  9. package/core/audits/byte-efficiency/render-blocking-resources.js +6 -3
  10. package/core/audits/byte-efficiency/unminified-css.js +2 -1
  11. package/core/audits/byte-efficiency/unminified-javascript.js +2 -1
  12. package/core/audits/byte-efficiency/unused-css-rules.js +1 -1
  13. package/core/audits/byte-efficiency/unused-javascript.js +2 -2
  14. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +1 -1
  15. package/core/audits/byte-efficiency/uses-optimized-images.js +1 -1
  16. package/core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  17. package/core/audits/byte-efficiency/uses-text-compression.js +2 -1
  18. package/core/audits/critical-request-chains.js +5 -3
  19. package/core/audits/dobetterweb/dom-size.js +1 -1
  20. package/core/audits/dobetterweb/uses-http2.js +1 -1
  21. package/core/audits/insights/cls-culprits-insight.js +1 -1
  22. package/core/audits/insights/document-latency-insight.js +1 -1
  23. package/core/audits/insights/dom-size-insight.js +1 -1
  24. package/core/audits/insights/{long-critical-network-tree-insight.d.ts → duplicated-javascript-insight.d.ts} +3 -3
  25. package/core/audits/insights/duplicated-javascript-insight.js +54 -0
  26. package/core/audits/insights/font-display-insight.js +8 -7
  27. package/core/audits/insights/forced-reflow-insight.d.ts +10 -0
  28. package/core/audits/insights/forced-reflow-insight.js +64 -12
  29. package/core/audits/insights/image-delivery-insight.js +9 -10
  30. package/core/audits/insights/insight-audit.js +9 -4
  31. package/core/audits/insights/interaction-to-next-paint-insight.js +1 -1
  32. package/core/audits/insights/lcp-discovery-insight.js +1 -1
  33. package/core/audits/insights/lcp-phases-insight.js +1 -1
  34. package/core/audits/insights/network-dependency-tree-insight.d.ts +11 -0
  35. package/core/audits/insights/{long-critical-network-tree-insight.js → network-dependency-tree-insight.js} +7 -7
  36. package/core/audits/insights/render-blocking-insight.js +1 -1
  37. package/core/audits/insights/slow-css-selector-insight.js +1 -1
  38. package/core/audits/insights/third-parties-insight.js +1 -1
  39. package/core/audits/insights/use-cache-insight.d.ts +11 -0
  40. package/core/audits/insights/use-cache-insight.js +61 -0
  41. package/core/audits/insights/viewport-insight.js +1 -1
  42. package/core/audits/largest-contentful-paint-element.js +7 -3
  43. package/core/audits/layout-shifts.js +5 -2
  44. package/core/audits/lcp-lazy-loaded.js +1 -1
  45. package/core/audits/long-tasks.js +6 -4
  46. package/core/audits/mainthread-work-breakdown.js +1 -1
  47. package/core/audits/metrics/first-contentful-paint.js +4 -2
  48. package/core/audits/metrics/interactive.js +6 -3
  49. package/core/audits/metrics/largest-contentful-paint.js +7 -3
  50. package/core/audits/metrics/max-potential-fid.js +6 -3
  51. package/core/audits/metrics/speed-index.js +6 -3
  52. package/core/audits/metrics/total-blocking-time.js +6 -3
  53. package/core/audits/metrics.js +4 -3
  54. package/core/audits/predictive-perf.js +4 -3
  55. package/core/audits/prioritize-lcp-image.js +5 -3
  56. package/core/audits/redirects.js +4 -2
  57. package/core/audits/script-treemap-data.js +8 -4
  58. package/core/audits/third-party-facades.js +1 -1
  59. package/core/audits/third-party-summary.js +1 -1
  60. package/core/audits/uses-rel-preconnect.js +7 -5
  61. package/core/audits/uses-rel-preload.js +5 -3
  62. package/core/computed/computed-artifact.d.ts +5 -1
  63. package/core/computed/computed-artifact.js +23 -2
  64. package/core/computed/critical-request-chains.d.ts +5 -1
  65. package/core/computed/critical-request-chains.js +4 -4
  66. package/core/computed/js-bundles.d.ts +1 -1
  67. package/core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
  68. package/core/computed/metrics/first-contentful-paint.js +1 -1
  69. package/core/computed/metrics/interactive.js +1 -1
  70. package/core/computed/metrics/lantern-first-contentful-paint.js +1 -1
  71. package/core/computed/metrics/lantern-interactive.js +1 -1
  72. package/core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  73. package/core/computed/metrics/lantern-max-potential-fid.js +1 -1
  74. package/core/computed/metrics/lantern-metric.js +1 -1
  75. package/core/computed/metrics/lantern-speed-index.js +1 -1
  76. package/core/computed/metrics/lantern-total-blocking-time.js +1 -1
  77. package/core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
  78. package/core/computed/metrics/largest-contentful-paint.js +1 -1
  79. package/core/computed/metrics/lcp-breakdown.js +1 -1
  80. package/core/computed/metrics/max-potential-fid.js +1 -1
  81. package/core/computed/metrics/metric.js +2 -0
  82. package/core/computed/metrics/speed-index.js +1 -1
  83. package/core/computed/metrics/time-to-first-byte.js +1 -1
  84. package/core/computed/metrics/timing-summary.d.ts +5 -2
  85. package/core/computed/metrics/timing-summary.js +8 -4
  86. package/core/computed/metrics/total-blocking-time.js +1 -1
  87. package/core/computed/module-duplication.d.ts +1 -1
  88. package/core/computed/navigation-insights.d.ts +11 -3
  89. package/core/computed/navigation-insights.js +7 -4
  90. package/core/computed/page-dependency-graph.d.ts +7 -3
  91. package/core/computed/page-dependency-graph.js +6 -5
  92. package/core/computed/tbt-impact-tasks.js +1 -1
  93. package/core/computed/trace-engine-result.d.ts +36 -2
  94. package/core/computed/trace-engine-result.js +119 -25
  95. package/core/computed/unused-javascript-summary.d.ts +2 -2
  96. package/core/computed/unused-javascript-summary.js +1 -1
  97. package/core/config/default-config.js +19 -15
  98. package/core/config/experimental-config.js +19 -0
  99. package/core/gather/gatherers/source-maps.d.ts +1 -0
  100. package/core/gather/gatherers/source-maps.js +3 -0
  101. package/core/gather/gatherers/trace-elements.d.ts +4 -4
  102. package/core/gather/gatherers/trace-elements.js +8 -4
  103. package/core/gather/gatherers/trace.js +5 -0
  104. package/core/lib/bf-cache-strings.d.ts +0 -122
  105. package/core/lib/bf-cache-strings.js +1 -2
  106. package/core/lib/deprecation-description.js +2 -1
  107. package/core/lib/legacy-javascript/legacy-javascript.d.ts +29 -0
  108. package/core/lib/legacy-javascript/legacy-javascript.js +351 -0
  109. package/core/lib/legacy-javascript/polyfill-graph-data.json +93 -0
  110. package/core/lib/legacy-javascript/polyfill-module-data.json +623 -0
  111. package/core/lib/trace-engine.d.ts +6 -1
  112. package/core/lib/trace-engine.js +1 -2
  113. package/package.json +10 -8
  114. package/shared/localization/locales/ar-XB.json +783 -513
  115. package/shared/localization/locales/ar.json +783 -513
  116. package/shared/localization/locales/bg.json +933 -663
  117. package/shared/localization/locales/ca.json +925 -655
  118. package/shared/localization/locales/cs.json +926 -656
  119. package/shared/localization/locales/da.json +926 -656
  120. package/shared/localization/locales/de.json +803 -533
  121. package/shared/localization/locales/el.json +928 -658
  122. package/shared/localization/locales/en-GB.json +929 -659
  123. package/shared/localization/locales/en-US.json +593 -551
  124. package/shared/localization/locales/en-XA.json +28 -508
  125. package/shared/localization/locales/en-XL.json +593 -551
  126. package/shared/localization/locales/es-419.json +928 -658
  127. package/shared/localization/locales/es.json +787 -517
  128. package/shared/localization/locales/fi.json +925 -655
  129. package/shared/localization/locales/fil.json +929 -659
  130. package/shared/localization/locales/fr.json +927 -657
  131. package/shared/localization/locales/he.json +795 -528
  132. package/shared/localization/locales/hi.json +798 -528
  133. package/shared/localization/locales/hr.json +929 -659
  134. package/shared/localization/locales/hu.json +926 -656
  135. package/shared/localization/locales/id.json +926 -656
  136. package/shared/localization/locales/it.json +930 -660
  137. package/shared/localization/locales/ja.json +927 -657
  138. package/shared/localization/locales/ko.json +936 -666
  139. package/shared/localization/locales/lt.json +933 -663
  140. package/shared/localization/locales/lv.json +809 -539
  141. package/shared/localization/locales/nl.json +925 -655
  142. package/shared/localization/locales/no.json +928 -658
  143. package/shared/localization/locales/pl.json +927 -657
  144. package/shared/localization/locales/pt-PT.json +841 -571
  145. package/shared/localization/locales/pt.json +841 -571
  146. package/shared/localization/locales/ro.json +925 -655
  147. package/shared/localization/locales/ru.json +935 -668
  148. package/shared/localization/locales/sk.json +925 -655
  149. package/shared/localization/locales/sl.json +927 -657
  150. package/shared/localization/locales/sr-Latn.json +925 -655
  151. package/shared/localization/locales/sr.json +925 -655
  152. package/shared/localization/locales/sv.json +936 -666
  153. package/shared/localization/locales/ta.json +935 -668
  154. package/shared/localization/locales/te.json +785 -515
  155. package/shared/localization/locales/th.json +813 -543
  156. package/shared/localization/locales/tr.json +927 -657
  157. package/shared/localization/locales/uk.json +795 -525
  158. package/shared/localization/locales/vi.json +929 -659
  159. package/shared/localization/locales/zh-HK.json +926 -656
  160. package/shared/localization/locales/zh-TW.json +784 -514
  161. package/shared/localization/locales/zh.json +926 -656
  162. package/tsconfig.json +2 -1
  163. package/types/artifacts.d.ts +2 -1
  164. package/core/audits/byte-efficiency/polyfill-graph-data.json +0 -53
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/UseCache.js';
8
+
9
+ import {Audit} from '../audit.js';
10
+ import * as i18n from '../../lib/i18n/i18n.js';
11
+ import {adaptInsightToAuditProduct} from './insight-audit.js';
12
+
13
+ // eslint-disable-next-line max-len
14
+ const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/UseCache.js', UIStrings);
15
+
16
+ class UseCacheInsight extends Audit {
17
+ /**
18
+ * @return {LH.Audit.Meta}
19
+ */
20
+ static get meta() {
21
+ return {
22
+ id: 'use-cache-insight',
23
+ title: str_(UIStrings.title),
24
+ failureTitle: str_(UIStrings.title),
25
+ description: str_(UIStrings.description),
26
+ guidanceLevel: 3,
27
+ requiredArtifacts: ['traces', 'SourceMaps'],
28
+ replacesAudits: ['uses-long-cache-ttl'],
29
+ };
30
+ }
31
+
32
+ /**
33
+ * @param {LH.Artifacts} artifacts
34
+ * @param {LH.Audit.Context} context
35
+ * @return {Promise<LH.Audit.Product>}
36
+ */
37
+ static async audit(artifacts, context) {
38
+ return adaptInsightToAuditProduct(artifacts, context, 'UseCache', (insight) => {
39
+ /** @type {LH.Audit.Details.Table['headings']} */
40
+ const headings = [
41
+ /* eslint-disable max-len */
42
+ {key: 'url', valueType: 'url', label: str_(UIStrings.requestColumn)},
43
+ {key: 'cacheLifetimeMs', valueType: 'ms', label: str_(UIStrings.cacheTTL), displayUnit: 'duration'},
44
+ {key: 'totalBytes', valueType: 'bytes', label: str_(i18n.UIStrings.columnTransferSize), displayUnit: 'kb', granularity: 1},
45
+ /* eslint-enable max-len */
46
+ ];
47
+ /** @type {LH.Audit.Details.Table['items']} */
48
+ const items = insight.requests.map(request => ({
49
+ url: request.request.args.data.url,
50
+ cacheLifetimeMs: request.ttl * 1000,
51
+ totalBytes: request.request.args.data.encodedDataLength,
52
+ }));
53
+ return Audit.makeTableDetails(headings, items, {
54
+ sortedBy: ['totalBytes'],
55
+ skipSumming: ['cacheLifetimeMs'],
56
+ });
57
+ });
58
+ }
59
+ }
60
+
61
+ export default UseCacheInsight;
@@ -24,7 +24,7 @@ class ViewportInsight extends Audit {
24
24
  failureTitle: str_(UIStrings.title),
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
- requiredArtifacts: ['traces', 'TraceElements'],
27
+ requiredArtifacts: ['traces', 'TraceElements', 'SourceMaps'],
28
28
  replacesAudits: ['viewport'],
29
29
  };
30
30
  }
@@ -50,7 +50,8 @@ class LargestContentfulPaintElement extends Audit {
50
50
  guidanceLevel: 1,
51
51
  supportedModes: ['navigation'],
52
52
  requiredArtifacts:
53
- ['traces', 'TraceElements', 'devtoolsLogs', 'GatherContext', 'settings', 'URL'],
53
+ ['traces', 'TraceElements', 'devtoolsLogs', 'GatherContext', 'settings', 'URL',
54
+ 'SourceMaps'],
54
55
  };
55
56
  }
56
57
 
@@ -122,8 +123,11 @@ class LargestContentfulPaintElement extends Audit {
122
123
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
123
124
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
124
125
  const gatherContext = artifacts.GatherContext;
125
- const metricComputationData = {trace, devtoolsLog, gatherContext,
126
- settings: context.settings, URL: artifacts.URL};
126
+ const metricComputationData = {
127
+ trace, devtoolsLog, gatherContext,
128
+ settings: context.settings, URL: artifacts.URL,
129
+ SourceMaps: artifacts.SourceMaps, simulator: null,
130
+ };
127
131
 
128
132
  const elementTable = this.makeElementTable(artifacts);
129
133
  if (!elementTable) {
@@ -47,7 +47,7 @@ class LayoutShifts extends Audit {
47
47
  description: str_(UIStrings.description),
48
48
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
49
49
  guidanceLevel: 2,
50
- requiredArtifacts: ['traces', 'TraceElements'],
50
+ requiredArtifacts: ['traces', 'TraceElements', 'SourceMaps'],
51
51
  };
52
52
  }
53
53
 
@@ -57,8 +57,11 @@ class LayoutShifts extends Audit {
57
57
  * @return {Promise<LH.Audit.Product>}
58
58
  */
59
59
  static async audit(artifacts, context) {
60
+ const settings = context.settings;
60
61
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
61
- const traceEngineResult = await TraceEngineResult.request({trace}, context);
62
+ const SourceMaps = artifacts.SourceMaps;
63
+ const traceEngineResult =
64
+ await TraceEngineResult.request({trace, settings, SourceMaps}, context);
62
65
  const clusters = traceEngineResult.data.LayoutShifts.clusters ?? [];
63
66
  const {cumulativeLayoutShift: clsSavings, impactByNodeId} =
64
67
  await CumulativeLayoutShiftComputed.request(trace, context);
@@ -36,7 +36,7 @@ class LargestContentfulPaintLazyLoaded extends Audit {
36
36
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
37
37
  guidanceLevel: 3,
38
38
  requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements',
39
- 'traces', 'devtoolsLogs', 'GatherContext', 'URL'],
39
+ 'traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
40
40
  };
41
41
  }
42
42
 
@@ -68,7 +68,7 @@ class LongTasks extends Audit {
68
68
  scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
69
69
  title: str_(UIStrings.title),
70
70
  description: str_(UIStrings.description),
71
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
71
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext', 'SourceMaps'],
72
72
  guidanceLevel: 1,
73
73
  };
74
74
  }
@@ -176,7 +176,7 @@ class LongTasks extends Audit {
176
176
  */
177
177
  static async audit(artifacts, context) {
178
178
  const settings = context.settings || {};
179
- const URL = artifacts.URL;
179
+ const {URL, SourceMaps} = artifacts;
180
180
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
181
181
  const tasks = await MainThreadTasks.request(trace, context);
182
182
  const devtoolsLog = artifacts.devtoolsLogs[LongTasks.DEFAULT_PASS];
@@ -192,9 +192,11 @@ class LongTasks extends Audit {
192
192
  taskTimingsByEvent = new Map();
193
193
 
194
194
  const simulatorOptions = {devtoolsLog, settings: context.settings};
195
- const pageGraph = await PageDependencyGraph.request({trace, devtoolsLog, URL}, context);
195
+ const pageGraph =
196
+ // eslint-disable-next-line max-len
197
+ await PageDependencyGraph.request({settings, trace, devtoolsLog, URL, SourceMaps, fromTrace: false}, context);
196
198
  const simulator = await LoadSimulator.request(simulatorOptions, context);
197
- const simulation = await simulator.simulate(pageGraph, {label: 'long-tasks-diagnostic'});
199
+ const simulation = simulator.simulate(pageGraph, {label: 'long-tasks-diagnostic'});
198
200
  for (const [node, timing] of simulation.nodeTimings.entries()) {
199
201
  if (node.type !== 'cpu') continue;
200
202
  taskTimingsByEvent.set(node.event, timing);
@@ -46,7 +46,7 @@ class MainThreadWorkBreakdown extends Audit {
46
46
  description: str_(UIStrings.description),
47
47
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
48
48
  guidanceLevel: 1,
49
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
49
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext', 'SourceMaps'],
50
50
  };
51
51
  }
52
52
 
@@ -27,7 +27,7 @@ class FirstContentfulPaint extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
29
29
  supportedModes: ['navigation'],
30
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
30
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
31
31
  };
32
32
  }
33
33
 
@@ -65,7 +65,9 @@ class FirstContentfulPaint extends Audit {
65
65
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
66
66
  const gatherContext = artifacts.GatherContext;
67
67
  const metricComputationData = {trace, devtoolsLog, gatherContext,
68
- settings: context.settings, URL: artifacts.URL};
68
+ settings: context.settings, URL: artifacts.URL,
69
+ SourceMaps: artifacts.SourceMaps, simulator: null,
70
+ };
69
71
  const metricResult = await ComputedFcp.request(metricComputationData, context);
70
72
  const options = context.options[context.settings.formFactor];
71
73
 
@@ -33,7 +33,7 @@ class InteractiveMetric extends Audit {
33
33
  description: str_(UIStrings.description),
34
34
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
35
35
  supportedModes: ['navigation'],
36
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
36
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
37
37
  };
38
38
  }
39
39
 
@@ -70,8 +70,11 @@ class InteractiveMetric extends Audit {
70
70
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
71
71
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
72
72
  const gatherContext = artifacts.GatherContext;
73
- const metricComputationData = {trace, devtoolsLog, gatherContext,
74
- settings: context.settings, URL: artifacts.URL};
73
+ const metricComputationData = {
74
+ trace, devtoolsLog, gatherContext,
75
+ settings: context.settings, URL: artifacts.URL,
76
+ SourceMaps: artifacts.SourceMaps, simulator: null,
77
+ };
75
78
  const metricResult = await Interactive.request(metricComputationData, context);
76
79
  const timeInMs = metricResult.timing;
77
80
  const options = context.options[context.settings.formFactor];
@@ -27,7 +27,8 @@ class LargestContentfulPaint extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
29
29
  supportedModes: ['navigation'],
30
- requiredArtifacts: ['HostUserAgent', 'traces', 'devtoolsLogs', 'GatherContext', 'URL'],
30
+ requiredArtifacts: ['HostUserAgent', 'traces', 'devtoolsLogs', 'GatherContext', 'URL',
31
+ 'SourceMaps'],
31
32
  };
32
33
  }
33
34
 
@@ -72,8 +73,11 @@ class LargestContentfulPaint extends Audit {
72
73
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
73
74
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
74
75
  const gatherContext = artifacts.GatherContext;
75
- const metricComputationData = {trace, devtoolsLog, gatherContext,
76
- settings: context.settings, URL: artifacts.URL};
76
+ const metricComputationData = {
77
+ trace, devtoolsLog, gatherContext,
78
+ settings: context.settings, URL: artifacts.URL,
79
+ SourceMaps: artifacts.SourceMaps, simulator: null,
80
+ };
77
81
 
78
82
  const metricResult = await ComputedLcp.request(metricComputationData, context);
79
83
  const options = context.options[context.settings.formFactor];
@@ -43,7 +43,7 @@ class MaxPotentialFID extends Audit {
43
43
  description: str_(UIStrings.description),
44
44
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
45
45
  supportedModes: ['navigation'],
46
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
46
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
47
47
  };
48
48
  }
49
49
 
@@ -120,8 +120,11 @@ class MaxPotentialFID extends Audit {
120
120
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
121
121
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
122
122
  const gatherContext = artifacts.GatherContext;
123
- const metricComputationData = {trace, devtoolsLog, gatherContext,
124
- settings: context.settings, URL: artifacts.URL};
123
+ const metricComputationData = {
124
+ trace, devtoolsLog, gatherContext,
125
+ settings: context.settings, URL: artifacts.URL,
126
+ SourceMaps: artifacts.SourceMaps, simulator: null,
127
+ };
125
128
  const metricResult = await ComputedFid.request(metricComputationData, context);
126
129
 
127
130
  const processedTrace = await ProcessedTrace.request(trace, context);
@@ -27,7 +27,7 @@ class SpeedIndex extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
29
29
  supportedModes: ['navigation'],
30
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
30
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
31
31
  };
32
32
  }
33
33
 
@@ -66,8 +66,11 @@ class SpeedIndex extends Audit {
66
66
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
67
67
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
68
68
  const gatherContext = artifacts.GatherContext;
69
- const metricComputationData = {trace, devtoolsLog, gatherContext,
70
- settings: context.settings, URL: artifacts.URL};
69
+ const metricComputationData = {
70
+ trace, devtoolsLog, gatherContext,
71
+ settings: context.settings, URL: artifacts.URL,
72
+ SourceMaps: artifacts.SourceMaps, simulator: null,
73
+ };
71
74
  const metricResult = await ComputedSi.request(metricComputationData, context);
72
75
  const options = context.options[context.settings.formFactor];
73
76
 
@@ -27,7 +27,7 @@ class TotalBlockingTime extends Audit {
27
27
  title: str_(i18n.UIStrings.totalBlockingTimeMetric),
28
28
  description: str_(UIStrings.description),
29
29
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
30
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
30
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
31
31
  };
32
32
  }
33
33
 
@@ -88,8 +88,11 @@ class TotalBlockingTime extends Audit {
88
88
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
89
89
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
90
90
  const gatherContext = artifacts.GatherContext;
91
- const metricComputationData = {trace, devtoolsLog, gatherContext,
92
- settings: context.settings, URL: artifacts.URL};
91
+ const metricComputationData = {
92
+ trace, devtoolsLog, gatherContext,
93
+ settings: context.settings, URL: artifacts.URL,
94
+ SourceMaps: artifacts.SourceMaps, simulator: null,
95
+ };
93
96
  if (
94
97
  gatherContext.gatherMode === 'timespan' &&
95
98
  context.settings.throttlingMethod === 'simulate'
@@ -26,7 +26,7 @@ class Metrics extends Audit {
26
26
  title: 'Metrics',
27
27
  description: 'Collects all available metrics.',
28
28
  supportedModes: ['navigation'],
29
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
29
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
30
30
  };
31
31
  }
32
32
 
@@ -36,12 +36,13 @@ class Metrics extends Audit {
36
36
  * @return {Promise<LH.Audit.Product>}
37
37
  */
38
38
  static async audit(artifacts, context) {
39
+ const settings = context.settings;
39
40
  const gatherContext = artifacts.GatherContext;
40
41
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
41
42
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
42
- const URL = artifacts.URL;
43
+ const {URL, SourceMaps} = artifacts;
43
44
  const summary = await TimingSummary
44
- .request({trace, devtoolsLog, gatherContext, settings: context.settings, URL}, context);
45
+ .request({trace, devtoolsLog, gatherContext, settings, URL, SourceMaps}, context);
45
46
  const metrics = summary.metrics;
46
47
  const debugInfo = summary.debugInfo;
47
48
 
@@ -33,7 +33,7 @@ class PredictivePerf extends Audit {
33
33
  'a cellular connection on a mobile device.',
34
34
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
35
35
  supportedModes: ['navigation'],
36
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL'],
36
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'SourceMaps'],
37
37
  };
38
38
  }
39
39
 
@@ -46,10 +46,11 @@ class PredictivePerf extends Audit {
46
46
  const gatherContext = artifacts.GatherContext;
47
47
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
48
48
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
49
- const URL = artifacts.URL;
49
+ const {URL, SourceMaps} = artifacts;
50
50
  /** @type {LH.Config.Settings} */
51
51
  const settings = JSON.parse(JSON.stringify(defaultSettings)); // Use default settings.
52
- const computationData = {trace, devtoolsLog, gatherContext, settings, URL};
52
+ const computationData =
53
+ {trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
53
54
  const fcp = await LanternFirstContentfulPaint.request(computationData, context);
54
55
  const tti = await LanternInteractive.request(computationData, context);
55
56
  const si = await LanternSpeedIndex.request(computationData, context);
@@ -38,7 +38,8 @@ class PrioritizeLcpImage extends Audit {
38
38
  description: str_(UIStrings.description),
39
39
  supportedModes: ['navigation'],
40
40
  guidanceLevel: 4,
41
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements'],
41
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements',
42
+ 'SourceMaps'],
42
43
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
43
44
  };
44
45
  }
@@ -237,9 +238,10 @@ class PrioritizeLcpImage extends Audit {
237
238
  const gatherContext = artifacts.GatherContext;
238
239
  const trace = artifacts.traces[PrioritizeLcpImage.DEFAULT_PASS];
239
240
  const devtoolsLog = artifacts.devtoolsLogs[PrioritizeLcpImage.DEFAULT_PASS];
240
- const URL = artifacts.URL;
241
+ const {URL, SourceMaps} = artifacts;
241
242
  const settings = context.settings;
242
- const metricData = {trace, devtoolsLog, gatherContext, settings, URL};
243
+ const metricData =
244
+ {trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
243
245
  const lcpElement = artifacts.TraceElements
244
246
  .find(element => element.traceEventType === 'largest-contentful-paint');
245
247
 
@@ -31,7 +31,7 @@ class Redirects extends Audit {
31
31
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
32
32
  supportedModes: ['navigation'],
33
33
  guidanceLevel: 2,
34
- requiredArtifacts: ['URL', 'GatherContext', 'devtoolsLogs', 'traces'],
34
+ requiredArtifacts: ['URL', 'GatherContext', 'devtoolsLogs', 'traces', 'SourceMaps'],
35
35
  };
36
36
  }
37
37
 
@@ -87,12 +87,14 @@ class Redirects extends Audit {
87
87
  const trace = artifacts.traces[Audit.DEFAULT_PASS];
88
88
  const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
89
89
  const gatherContext = artifacts.GatherContext;
90
+ const {URL, SourceMaps} = artifacts;
90
91
 
91
92
  const processedTrace = await ProcessedTrace.request(trace, context);
92
93
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
93
94
  const documentRequests = Redirects.getDocumentRequestChain(networkRecords, processedTrace);
94
95
 
95
- const metricComputationData = {trace, devtoolsLog, gatherContext, settings, URL: artifacts.URL};
96
+ const metricComputationData =
97
+ {trace, devtoolsLog, gatherContext, settings, URL, SourceMaps, simulator: null};
96
98
  const metricResult = await LanternInteractive.request(metricComputationData, context);
97
99
 
98
100
  /** @type {Map<string, LH.Gatherer.Simulation.NodeTiming>} */
@@ -31,7 +31,7 @@ class ScriptTreemapDataAudit extends Audit {
31
31
  title: 'Script Treemap Data',
32
32
  description: 'Used for treemap app',
33
33
  requiredArtifacts:
34
- ['traces', 'devtoolsLogs', 'SourceMaps', 'Scripts', 'JsUsage', 'URL'],
34
+ ['traces', 'devtoolsLogs', 'SourceMaps', 'Scripts', 'JsUsage', 'URL', 'SourceMaps'],
35
35
  };
36
36
  }
37
37
 
@@ -114,8 +114,12 @@ class ScriptTreemapDataAudit extends Audit {
114
114
  */
115
115
  function collapseAll(node) {
116
116
  while (node.children && node.children.length === 1) {
117
- node.name += '/' + node.children[0].name;
118
- node.children = node.children[0].children;
117
+ const child = node.children[0];
118
+ node.name += '/' + child.name;
119
+ if (child.duplicatedNormalizedModuleName) {
120
+ node.duplicatedNormalizedModuleName = child.duplicatedNormalizedModuleName;
121
+ }
122
+ node.children = child.children;
119
123
  }
120
124
 
121
125
  if (node.children) {
@@ -174,7 +178,7 @@ class ScriptTreemapDataAudit extends Audit {
174
178
  if (script.scriptLanguage !== 'JavaScript') continue;
175
179
 
176
180
  const name = script.url;
177
- const bundle = bundles.find(bundle => script.scriptId === bundle.script.scriptId);
181
+ const bundle = bundles.find(bundle => script.scriptId === bundle.script.scriptId) ?? null;
178
182
  const scriptCoverage = /** @type {LH.Artifacts['JsUsage'][string] | undefined} */
179
183
  (artifacts.JsUsage[script.scriptId]);
180
184
  const unusedJavascriptSummary = scriptCoverage ?
@@ -87,7 +87,7 @@ class ThirdPartyFacades extends Audit {
87
87
  description: str_(UIStrings.description),
88
88
  supportedModes: ['navigation'],
89
89
  guidanceLevel: 3,
90
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
90
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext', 'SourceMaps'],
91
91
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
92
92
  };
93
93
  }
@@ -67,7 +67,7 @@ class ThirdPartySummary extends Audit {
67
67
  description: str_(UIStrings.description),
68
68
  guidanceLevel: 1,
69
69
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
70
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
70
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext', 'SourceMaps'],
71
71
  };
72
72
  }
73
73
 
@@ -62,7 +62,7 @@ class UsesRelPreconnectAudit extends Audit {
62
62
  description: str_(UIStrings.description),
63
63
  supportedModes: ['navigation'],
64
64
  guidanceLevel: 3,
65
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'LinkElements'],
65
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'LinkElements', 'SourceMaps'],
66
66
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
67
67
  };
68
68
  }
@@ -124,6 +124,7 @@ class UsesRelPreconnectAudit extends Audit {
124
124
  static async audit(artifacts, context) {
125
125
  const trace = artifacts.traces[UsesRelPreconnectAudit.DEFAULT_PASS];
126
126
  const devtoolsLog = artifacts.devtoolsLogs[UsesRelPreconnectAudit.DEFAULT_PASS];
127
+ const {URL, SourceMaps} = artifacts;
127
128
  const settings = context.settings;
128
129
 
129
130
  let maxWastedLcp = 0;
@@ -134,15 +135,16 @@ class UsesRelPreconnectAudit extends Audit {
134
135
  const [networkRecords, mainResource, loadSimulator, processedNavigation, pageGraph] =
135
136
  await Promise.all([
136
137
  NetworkRecords.request(devtoolsLog, context),
137
- MainResource.request({devtoolsLog, URL: artifacts.URL}, context),
138
+ MainResource.request({devtoolsLog, URL}, context),
138
139
  LoadSimulator.request({devtoolsLog, settings}, context),
139
140
  ProcessedNavigation.request(trace, context),
140
- PageDependencyGraph.request({trace, devtoolsLog, URL: artifacts.URL}, context),
141
+ PageDependencyGraph.request(
142
+ {settings, trace, devtoolsLog, URL, SourceMaps, fromTrace: false}, context),
141
143
  ]);
142
144
 
143
145
  const {rtt, additionalRttByOrigin} = loadSimulator.getOptions();
144
146
  const lcpGraph =
145
- await LanternLargestContentfulPaint.getPessimisticGraph(pageGraph, processedNavigation);
147
+ LanternLargestContentfulPaint.getPessimisticGraph(pageGraph, processedNavigation);
146
148
  /** @type {Set<string>} */
147
149
  const lcpGraphURLs = new Set();
148
150
  lcpGraph.traverse(node => {
@@ -150,7 +152,7 @@ class UsesRelPreconnectAudit extends Audit {
150
152
  });
151
153
 
152
154
  const fcpGraph =
153
- await LanternFirstContentfulPaint.getPessimisticGraph(pageGraph, processedNavigation);
155
+ LanternFirstContentfulPaint.getPessimisticGraph(pageGraph, processedNavigation);
154
156
  const fcpGraphURLs = new Set();
155
157
  fcpGraph.traverse(node => {
156
158
  if (node.type === 'network') fcpGraphURLs.add(node.request.url);
@@ -44,7 +44,7 @@ class UsesRelPreloadAudit extends Audit {
44
44
  description: str_(UIStrings.description),
45
45
  supportedModes: ['navigation'],
46
46
  guidanceLevel: 3,
47
- requiredArtifacts: ['devtoolsLogs', 'traces', 'URL'],
47
+ requiredArtifacts: ['devtoolsLogs', 'traces', 'URL', 'SourceMaps'],
48
48
  scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
49
49
  };
50
50
  }
@@ -212,14 +212,16 @@ class UsesRelPreloadAudit extends Audit {
212
212
  * @return {Promise<LH.Audit.Product>}
213
213
  */
214
214
  static async audit(artifacts, context) {
215
+ const settings = context.settings;
215
216
  const trace = artifacts.traces[UsesRelPreloadAudit.DEFAULT_PASS];
216
217
  const devtoolsLog = artifacts.devtoolsLogs[UsesRelPreloadAudit.DEFAULT_PASS];
217
- const URL = artifacts.URL;
218
+ const {URL, SourceMaps} = artifacts;
218
219
  const simulatorOptions = {devtoolsLog, settings: context.settings};
219
220
 
220
221
  const [mainResource, graph, simulator] = await Promise.all([
221
222
  MainResource.request({devtoolsLog, URL}, context),
222
- PageDependencyGraph.request({trace, devtoolsLog, URL}, context),
223
+ PageDependencyGraph.request(
224
+ {settings, trace, devtoolsLog, URL, SourceMaps, fromTrace: false}, context),
223
225
  LoadSimulator.request(simulatorOptions, context),
224
226
  ]);
225
227
 
@@ -4,7 +4,11 @@
4
4
  * @template {{name: string, compute_(dependencies: unknown, context: LH.Artifacts.ComputedContext): Promise<unknown>}} C
5
5
  * @template {Array<keyof LH.Util.FirstParamType<C['compute_']>>} K
6
6
  * @param {C} computableArtifact
7
- * @param {(K & ([keyof LH.Util.FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys List of properties of `dependencies` used by `compute_`; other properties are filtered out. Use `null` to allow all properties. Ensures that only required properties are used for caching result.
7
+ * @param {(K & ([keyof LH.Util.FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys
8
+ * List of properties of `dependencies` used by `compute_`; other properties are filtered out.
9
+ * Use `null` to allow all properties. Ensures that only required properties are used for caching result.
10
+ * For optional properties of `dependencies`, undefined cannot be used and if found is treated as an error.
11
+ * This is to guard against developer mistakes. For optional properties, make it nullable instead.
8
12
  */
9
13
  export function makeComputedArtifact<C extends {
10
14
  name: string;
@@ -7,6 +7,7 @@
7
7
  import log from 'lighthouse-logger';
8
8
 
9
9
  import {ArbitraryEqualityMap} from '../lib/arbitrary-equality-map.js';
10
+ import {isUnderTest} from '../lib/lh-env.js';
10
11
 
11
12
  /**
12
13
  * Decorate computableArtifact with a caching `request()` method which will
@@ -14,7 +15,11 @@ import {ArbitraryEqualityMap} from '../lib/arbitrary-equality-map.js';
14
15
  * @template {{name: string, compute_(dependencies: unknown, context: LH.Artifacts.ComputedContext): Promise<unknown>}} C
15
16
  * @template {Array<keyof LH.Util.FirstParamType<C['compute_']>>} K
16
17
  * @param {C} computableArtifact
17
- * @param {(K & ([keyof LH.Util.FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys List of properties of `dependencies` used by `compute_`; other properties are filtered out. Use `null` to allow all properties. Ensures that only required properties are used for caching result.
18
+ * @param {(K & ([keyof LH.Util.FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys
19
+ * List of properties of `dependencies` used by `compute_`; other properties are filtered out.
20
+ * Use `null` to allow all properties. Ensures that only required properties are used for caching result.
21
+ * For optional properties of `dependencies`, undefined cannot be used and if found is treated as an error.
22
+ * This is to guard against developer mistakes. For optional properties, make it nullable instead.
18
23
  */
19
24
  function makeComputedArtifact(computableArtifact, keys) {
20
25
  // tsc (3.1) has more difficulty with template inter-references in jsdoc, so
@@ -27,13 +32,29 @@ function makeComputedArtifact(computableArtifact, keys) {
27
32
  * @return {ReturnType<C['compute_']>}
28
33
  */
29
34
  const request = (dependencies, context) => {
35
+ const computedName = computableArtifact.name;
36
+
37
+ // Guard against missing properties. Optional properties must be passed as null - if missing or
38
+ // undefined, throw an error.
39
+ for (const key of keys || []) {
40
+ if (dependencies && typeof dependencies === 'object' && dependencies[key] === undefined) {
41
+ // eslint-disable-next-line max-len
42
+ const err = new Error(`missing required key "${String(key)}" for computed artifact ${computableArtifact.name}`);
43
+ if (isUnderTest) {
44
+ throw err;
45
+ } else {
46
+ // For now, simply log in production.
47
+ log.error(`lh:computed:${computedName}`, err);
48
+ }
49
+ }
50
+ }
51
+
30
52
  const pickedDependencies = keys ?
31
53
  Object.fromEntries(keys.map(key => [key, dependencies[key]])) :
32
54
  dependencies;
33
55
 
34
56
  // NOTE: break immutability solely for this caching-controller function.
35
57
  const computedCache = /** @type {Map<string, ArbitraryEqualityMap>} */ (context.computedCache);
36
- const computedName = computableArtifact.name;
37
58
 
38
59
  const cache = computedCache.get(computedName) || new ArbitraryEqualityMap();
39
60
  computedCache.set(computedName, cache);
@@ -2,8 +2,10 @@ export { CriticalRequestChainsComputed as CriticalRequestChains };
2
2
  declare const CriticalRequestChainsComputed: typeof CriticalRequestChains & {
3
3
  request: (dependencies: {
4
4
  URL: LH.Artifacts["URL"];
5
+ SourceMaps: LH.Artifacts["SourceMaps"];
5
6
  devtoolsLog: LH.DevtoolsLog;
6
7
  trace: LH.Trace;
8
+ settings: LH.Audit.Context["settings"];
7
9
  }, context: LH.Artifacts.ComputedContext) => ReturnType<typeof CriticalRequestChains.compute_>;
8
10
  };
9
11
  declare class CriticalRequestChains {
@@ -24,14 +26,16 @@ declare class CriticalRequestChains {
24
26
  */
25
27
  static extractChainsFromGraph(mainResource: LH.Artifacts.NetworkRequest, graph: LH.Gatherer.Simulation.GraphNode): LH.Artifacts.CriticalRequestNode;
26
28
  /**
27
- * @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog, trace: LH.Trace}} data
29
+ * @param {{URL: LH.Artifacts['URL'], SourceMaps: LH.Artifacts['SourceMaps'], devtoolsLog: LH.DevtoolsLog, trace: LH.Trace, settings: LH.Audit.Context['settings']}} data
28
30
  * @param {LH.Artifacts.ComputedContext} context
29
31
  * @return {Promise<LH.Artifacts.CriticalRequestNode>}
30
32
  */
31
33
  static compute_(data: {
32
34
  URL: LH.Artifacts["URL"];
35
+ SourceMaps: LH.Artifacts["SourceMaps"];
33
36
  devtoolsLog: LH.DevtoolsLog;
34
37
  trace: LH.Trace;
38
+ settings: LH.Audit.Context["settings"];
35
39
  }, context: LH.Artifacts.ComputedContext): Promise<LH.Artifacts.CriticalRequestNode>;
36
40
  }
37
41
  import * as Lantern from '../lib/lantern/lantern.js';