lighthouse 12.3.0-dev.20250204 → 12.3.0-dev.20250206

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 (53) hide show
  1. package/core/audits/audit.d.ts +5 -0
  2. package/core/audits/audit.js +11 -0
  3. package/core/audits/insights/README.md +3 -0
  4. package/core/audits/insights/cls-culprits-insight.d.ts +11 -0
  5. package/core/audits/insights/cls-culprits-insight.js +52 -0
  6. package/core/audits/insights/document-latency-insight.d.ts +11 -0
  7. package/core/audits/insights/document-latency-insight.js +47 -0
  8. package/core/audits/insights/dom-size-insight.d.ts +11 -0
  9. package/core/audits/insights/dom-size-insight.js +84 -0
  10. package/core/audits/insights/font-display-insight.d.ts +11 -0
  11. package/core/audits/insights/font-display-insight.js +52 -0
  12. package/core/audits/insights/forced-reflow-insight.d.ts +11 -0
  13. package/core/audits/insights/forced-reflow-insight.js +52 -0
  14. package/core/audits/insights/image-delivery-insight.d.ts +11 -0
  15. package/core/audits/insights/image-delivery-insight.js +52 -0
  16. package/core/audits/insights/insight-audit.d.ts +16 -0
  17. package/core/audits/insights/insight-audit.js +88 -0
  18. package/core/audits/insights/interaction-to-next-paint-insight.d.ts +11 -0
  19. package/core/audits/insights/interaction-to-next-paint-insight.js +52 -0
  20. package/core/audits/insights/lcp-discovery-insight.d.ts +11 -0
  21. package/core/audits/insights/lcp-discovery-insight.js +47 -0
  22. package/core/audits/insights/lcp-phases-insight.d.ts +11 -0
  23. package/core/audits/insights/lcp-phases-insight.js +52 -0
  24. package/core/audits/insights/long-critical-network-tree-insight.d.ts +11 -0
  25. package/core/audits/insights/long-critical-network-tree-insight.js +52 -0
  26. package/core/audits/insights/render-blocking-insight.d.ts +11 -0
  27. package/core/audits/insights/render-blocking-insight.js +56 -0
  28. package/core/audits/insights/slow-css-selector-insight.d.ts +11 -0
  29. package/core/audits/insights/slow-css-selector-insight.js +52 -0
  30. package/core/audits/insights/third-parties-insight.d.ts +11 -0
  31. package/core/audits/insights/third-parties-insight.js +52 -0
  32. package/core/audits/insights/viewport-insight.d.ts +11 -0
  33. package/core/audits/insights/viewport-insight.js +53 -0
  34. package/core/computed/metrics/lantern-metric.js +5 -1
  35. package/core/computed/trace-engine-result.d.ts +0 -4
  36. package/core/computed/trace-engine-result.js +0 -26
  37. package/core/config/default-config.js +35 -0
  38. package/core/gather/gatherers/trace-elements.d.ts +8 -0
  39. package/core/gather/gatherers/trace-elements.js +71 -1
  40. package/core/runner.js +2 -0
  41. package/dist/report/bundle.esm.js +38 -3
  42. package/dist/report/flow.js +40 -5
  43. package/dist/report/standalone.js +38 -3
  44. package/package.json +4 -3
  45. package/report/assets/styles.css +35 -0
  46. package/report/renderer/components.js +1 -1
  47. package/report/renderer/details-renderer.d.ts +5 -0
  48. package/report/renderer/details-renderer.js +21 -0
  49. package/report/renderer/performance-category-renderer.js +24 -9
  50. package/shared/localization/locales/en-US.json +213 -0
  51. package/shared/localization/locales/en-XL.json +213 -0
  52. package/types/artifacts.d.ts +1 -1
  53. package/types/lhr/audit-details.d.ts +6 -0
@@ -65,6 +65,71 @@ class TraceElements extends BaseGatherer {
65
65
  if (name) this.animationIdToName.set(id, name);
66
66
  }
67
67
 
68
+ /**
69
+ * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
70
+ * @param {string|undefined} navigationId
71
+ * @return {Promise<Array<{nodeId: number}>>}
72
+ */
73
+ static async getTraceEngineElements(traceEngineResult, navigationId) {
74
+ // Can only resolve elements for the latest insight set, which should correspond
75
+ // to the current navigation id (if present). Can't resolve elements for pages
76
+ // that are gone.
77
+ const insightSet = [...traceEngineResult.insights.values()].at(-1);
78
+ if (!insightSet) {
79
+ return [];
80
+ }
81
+
82
+ if (navigationId) {
83
+ if (insightSet.navigation?.args.data?.navigationId !== navigationId) {
84
+ return [];
85
+ }
86
+ } else {
87
+ if (insightSet.navigation) {
88
+ return [];
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Execute `cb(obj, key)` on every object property (non-objects only), recursively.
94
+ * @param {any} obj
95
+ * @param {(obj: Record<string, string>, key: string) => void} cb
96
+ * @param {Set<object>} seen
97
+ */
98
+ function recursiveObjectEnumerate(obj, cb, seen) {
99
+ if (seen.has(seen)) {
100
+ return;
101
+ }
102
+
103
+ seen.add(obj);
104
+
105
+ if (obj && typeof obj === 'object' && !Array.isArray(obj)) {
106
+ Object.keys(obj).forEach(key => {
107
+ if (typeof obj[key] === 'object') {
108
+ recursiveObjectEnumerate(obj[key], cb, seen);
109
+ } else {
110
+ cb(obj, key);
111
+ }
112
+ });
113
+ } else if (Array.isArray(obj)) {
114
+ obj.forEach(item => {
115
+ if (typeof item === 'object' || Array.isArray(item)) {
116
+ recursiveObjectEnumerate(item, cb, seen);
117
+ }
118
+ });
119
+ }
120
+ }
121
+
122
+ /** @type {number[]} */
123
+ const nodeIds = [];
124
+ recursiveObjectEnumerate(insightSet.model, (obj, key) => {
125
+ if (typeof obj[key] === 'number' && (key === 'nodeId' || key === 'node_id')) {
126
+ nodeIds.push(obj[key]);
127
+ }
128
+ }, new Set());
129
+
130
+ return [...new Set(nodeIds)].map(id => ({nodeId: id}));
131
+ }
132
+
68
133
  /**
69
134
  * We want to a single representative node to represent the shift, so let's pick
70
135
  * the one with the largest impact (size x distance moved).
@@ -319,7 +384,10 @@ class TraceElements extends BaseGatherer {
319
384
 
320
385
  const processedTrace = await ProcessedTrace.request(trace, context);
321
386
  const {mainThreadEvents} = processedTrace;
387
+ const navigationId = processedTrace.timeOriginEvt.args.data?.navigationId;
322
388
 
389
+ const traceEngineData = await TraceElements.getTraceEngineElements(
390
+ traceEngineResult, navigationId);
323
391
  const lcpNodeData = await TraceElements.getLcpElement(trace, context);
324
392
  const shiftsData = await TraceElements.getTopLayoutShifts(
325
393
  trace, traceEngineResult.data, rootCauses, context);
@@ -328,6 +396,7 @@ class TraceElements extends BaseGatherer {
328
396
 
329
397
  /** @type {Map<string, TraceElementData[]>} */
330
398
  const backendNodeDataMap = new Map([
399
+ ['trace-engine', traceEngineData],
331
400
  ['largest-contentful-paint', lcpNodeData ? [lcpNodeData] : []],
332
401
  ['layout-shift', shiftsData],
333
402
  ['animation', animatedElementData],
@@ -336,6 +405,7 @@ class TraceElements extends BaseGatherer {
336
405
 
337
406
  /** @type {Map<number, LH.Crdp.Runtime.CallFunctionOnResponse | null>} */
338
407
  const callFunctionOnCache = new Map();
408
+ /** @type {LH.Artifacts.TraceElement[]} */
339
409
  const traceElements = [];
340
410
  for (const [traceEventType, backendNodeData] of backendNodeDataMap) {
341
411
  for (let i = 0; i < backendNodeData.length; i++) {
@@ -348,8 +418,8 @@ class TraceElements extends BaseGatherer {
348
418
 
349
419
  if (response?.result?.value) {
350
420
  traceElements.push({
351
- traceEventType,
352
421
  ...response.result.value,
422
+ traceEventType,
353
423
  animations: backendNodeData[i].animations,
354
424
  nodeId: backendNodeId,
355
425
  type: backendNodeData[i].type,
package/core/runner.js CHANGED
@@ -484,6 +484,7 @@ vs
484
484
  'multi-check-audit.js',
485
485
  'byte-efficiency/byte-efficiency-audit.js',
486
486
  'manual/manual-audit.js',
487
+ 'insights/insight-audit.js',
487
488
  ];
488
489
 
489
490
  const fileList = [
@@ -499,6 +500,7 @@ vs
499
500
  ...fs.readdirSync(path.join(moduleDir, './audits/byte-efficiency'))
500
501
  .map(f => `byte-efficiency/${f}`),
501
502
  ...fs.readdirSync(path.join(moduleDir, './audits/manual')).map(f => `manual/${f}`),
503
+ ...fs.readdirSync(path.join(moduleDir, './audits/insights')).map(f => `insights/${f}`),
502
504
  ];
503
505
  return fileList.filter(f => {
504
506
  return /\.js$/.test(f) && !ignoredFiles.includes(f);