lighthouse 12.3.0-dev.20250224 → 12.3.0-dev.20250225

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 (90) hide show
  1. package/core/audits/audit.js +1 -0
  2. package/core/audits/insights/cls-culprits-insight.d.ts +14 -0
  3. package/core/audits/insights/cls-culprits-insight.js +97 -12
  4. package/core/audits/insights/document-latency-insight.js +1 -0
  5. package/core/audits/insights/dom-size-insight.js +1 -0
  6. package/core/audits/insights/font-display-insight.js +1 -0
  7. package/core/audits/insights/image-delivery-insight.js +6 -0
  8. package/core/audits/insights/interaction-to-next-paint-insight.js +1 -0
  9. package/core/audits/insights/lcp-discovery-insight.js +1 -0
  10. package/core/audits/insights/lcp-phases-insight.js +1 -0
  11. package/core/audits/insights/long-critical-network-tree-insight.js +1 -0
  12. package/core/audits/insights/render-blocking-insight.js +1 -0
  13. package/core/audits/insights/third-parties-insight.js +1 -0
  14. package/core/audits/insights/viewport-insight.js +1 -0
  15. package/core/audits/layout-shifts.js +1 -1
  16. package/core/lib/bf-cache-strings.d.ts +7 -4
  17. package/core/lib/bf-cache-strings.js +174 -140
  18. package/core/lib/cdt/generated/ParsedURL.d.ts +1 -0
  19. package/core/lib/cdt/generated/ParsedURL.js +16 -4
  20. package/core/lib/cdt/generated/SourceMap.d.ts +32 -5
  21. package/core/lib/cdt/generated/SourceMap.js +192 -100
  22. package/core/lib/deprecations-strings.d.ts +78 -98
  23. package/core/lib/deprecations-strings.js +23 -41
  24. package/core/lib/i18n/i18n.d.ts +1 -0
  25. package/core/lib/i18n/i18n.js +2 -0
  26. package/dist/report/bundle.esm.js +164 -15
  27. package/dist/report/flow.js +159 -10
  28. package/dist/report/standalone.js +158 -9
  29. package/package.json +3 -3
  30. package/readme.md +1 -0
  31. package/report/assets/styles.css +141 -5
  32. package/report/assets/templates.html +13 -0
  33. package/report/renderer/components.js +2 -2
  34. package/report/renderer/details-renderer.js +3 -0
  35. package/report/renderer/dom.d.ts +12 -1
  36. package/report/renderer/dom.js +26 -1
  37. package/report/renderer/performance-category-renderer.d.ts +2 -2
  38. package/report/renderer/performance-category-renderer.js +58 -23
  39. package/report/renderer/topbar-features.js +4 -5
  40. package/shared/localization/locales/ar-XB.json +0 -21
  41. package/shared/localization/locales/ar.json +0 -21
  42. package/shared/localization/locales/bg.json +0 -21
  43. package/shared/localization/locales/ca.json +0 -21
  44. package/shared/localization/locales/cs.json +0 -21
  45. package/shared/localization/locales/da.json +0 -21
  46. package/shared/localization/locales/de.json +0 -21
  47. package/shared/localization/locales/el.json +0 -21
  48. package/shared/localization/locales/en-GB.json +0 -21
  49. package/shared/localization/locales/en-US.json +33 -24
  50. package/shared/localization/locales/en-XA.json +0 -21
  51. package/shared/localization/locales/en-XL.json +33 -24
  52. package/shared/localization/locales/es-419.json +0 -21
  53. package/shared/localization/locales/es.json +0 -21
  54. package/shared/localization/locales/fi.json +0 -21
  55. package/shared/localization/locales/fil.json +0 -21
  56. package/shared/localization/locales/fr.json +0 -21
  57. package/shared/localization/locales/he.json +0 -21
  58. package/shared/localization/locales/hi.json +0 -21
  59. package/shared/localization/locales/hr.json +0 -21
  60. package/shared/localization/locales/hu.json +0 -21
  61. package/shared/localization/locales/id.json +0 -21
  62. package/shared/localization/locales/it.json +0 -21
  63. package/shared/localization/locales/ja.json +0 -21
  64. package/shared/localization/locales/ko.json +0 -21
  65. package/shared/localization/locales/lt.json +0 -21
  66. package/shared/localization/locales/lv.json +0 -21
  67. package/shared/localization/locales/nl.json +0 -21
  68. package/shared/localization/locales/no.json +0 -21
  69. package/shared/localization/locales/pl.json +0 -21
  70. package/shared/localization/locales/pt-PT.json +0 -21
  71. package/shared/localization/locales/pt.json +0 -21
  72. package/shared/localization/locales/ro.json +0 -21
  73. package/shared/localization/locales/ru.json +0 -21
  74. package/shared/localization/locales/sk.json +0 -21
  75. package/shared/localization/locales/sl.json +0 -21
  76. package/shared/localization/locales/sr-Latn.json +0 -21
  77. package/shared/localization/locales/sr.json +0 -21
  78. package/shared/localization/locales/sv.json +0 -21
  79. package/shared/localization/locales/ta.json +0 -21
  80. package/shared/localization/locales/te.json +0 -21
  81. package/shared/localization/locales/th.json +0 -21
  82. package/shared/localization/locales/tr.json +0 -21
  83. package/shared/localization/locales/uk.json +0 -21
  84. package/shared/localization/locales/vi.json +0 -21
  85. package/shared/localization/locales/zh-HK.json +0 -21
  86. package/shared/localization/locales/zh-TW.json +0 -21
  87. package/shared/localization/locales/zh.json +0 -21
  88. package/types/audit.d.ts +2 -0
  89. package/types/lhr/audit-details.d.ts +5 -1
  90. package/types/lhr/audit-result.d.ts +2 -0
@@ -478,6 +478,7 @@ class Audit {
478
478
 
479
479
  details: product.details,
480
480
  guidanceLevel: audit.meta.guidanceLevel,
481
+ replacesAudits: audit.meta.replacesAudits,
481
482
  };
482
483
  }
483
484
 
@@ -1,5 +1,16 @@
1
1
  export default CLSCulpritsInsight;
2
+ export type SubItem = {
3
+ extra?: LH.Audit.Details.NodeValue | LH.Audit.Details.UrlValue;
4
+ cause: LH.IcuMessage;
5
+ };
2
6
  declare class CLSCulpritsInsight extends Audit {
7
+ /**
8
+ * @param {import('@paulirish/trace_engine/models/trace/insights/CLSCulprits.js').CLSCulpritsInsightModel} insight
9
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event
10
+ * @param {LH.Artifacts.TraceElement[]} TraceElements
11
+ * @return {LH.Audit.Details.TableSubItems|undefined}
12
+ */
13
+ static getCulpritSubItems(insight: import("@paulirish/trace_engine/models/trace/insights/CLSCulprits.js").CLSCulpritsInsightModel, event: import("../../lib/trace-engine.js").SaneSyntheticLayoutShift, TraceElements: LH.Artifacts.TraceElement[]): LH.Audit.Details.TableSubItems | undefined;
3
14
  /**
4
15
  * @param {LH.Artifacts} artifacts
5
16
  * @param {LH.Audit.Context} context
@@ -7,5 +18,8 @@ declare class CLSCulpritsInsight extends Audit {
7
18
  */
8
19
  static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
9
20
  }
21
+ export namespace UIStrings {
22
+ let columnScore: string;
23
+ }
10
24
  import { Audit } from '../audit.js';
11
25
  //# sourceMappingURL=cls-culprits-insight.d.ts.map
@@ -1,19 +1,32 @@
1
- /* eslint-disable no-unused-vars */ // TODO: remove once implemented.
2
-
3
1
  /**
4
2
  * @license
5
3
  * Copyright 2025 Google LLC
6
4
  * SPDX-License-Identifier: Apache-2.0
7
5
  */
8
6
 
9
- import {UIStrings} from '@paulirish/trace_engine/models/trace/insights/CLSCulprits.js';
7
+ import {UIStrings as InsightUIStrings} from '@paulirish/trace_engine/models/trace/insights/CLSCulprits.js';
10
8
 
11
9
  import {Audit} from '../audit.js';
12
10
  import * as i18n from '../../lib/i18n/i18n.js';
13
11
  import {adaptInsightToAuditProduct, makeNodeItemForNodeId} from './insight-audit.js';
12
+ import TraceElements from '../../gather/gatherers/trace-elements.js';
13
+ import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
14
+
15
+ const MAX_LAYOUT_SHIFTS_PER_CLUSTER = 5;
16
+
17
+ /** @typedef {{extra?: LH.Audit.Details.NodeValue | LH.Audit.Details.UrlValue, cause: LH.IcuMessage}} SubItem */
14
18
 
15
19
  // eslint-disable-next-line max-len
16
- const str_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js', UIStrings);
20
+ const insightStr_ = i18n.createIcuMessageFn('node_modules/@paulirish/trace_engine/models/trace/insights/CLSCulprits.js', InsightUIStrings);
21
+
22
+ /* eslint-disable max-len */
23
+ const UIStrings = {
24
+ /** Label for a column in a data table; entries in this column will be a number representing how large the layout shift was. */
25
+ columnScore: 'Layout shift score',
26
+ };
27
+ /* eslint-enable max-len */
28
+
29
+ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
17
30
 
18
31
  class CLSCulpritsInsight extends Audit {
19
32
  /**
@@ -22,31 +35,103 @@ class CLSCulpritsInsight extends Audit {
22
35
  static get meta() {
23
36
  return {
24
37
  id: 'cls-culprits-insight',
25
- title: str_(UIStrings.title),
26
- failureTitle: str_(UIStrings.title),
27
- description: str_(UIStrings.description),
38
+ title: insightStr_(InsightUIStrings.title),
39
+ failureTitle: insightStr_(InsightUIStrings.title),
40
+ description: insightStr_(InsightUIStrings.description),
28
41
  guidanceLevel: 3,
29
42
  requiredArtifacts: ['traces', 'TraceElements'],
43
+ replacesAudits: ['layout-shifts', 'non-composited-animations', 'unsized-images'],
30
44
  };
31
45
  }
32
46
 
47
+ /**
48
+ * @param {import('@paulirish/trace_engine/models/trace/insights/CLSCulprits.js').CLSCulpritsInsightModel} insight
49
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event
50
+ * @param {LH.Artifacts.TraceElement[]} TraceElements
51
+ * @return {LH.Audit.Details.TableSubItems|undefined}
52
+ */
53
+ static getCulpritSubItems(insight, event, TraceElements) {
54
+ const culprits = insight.shifts.get(event);
55
+ if (!culprits) {
56
+ return;
57
+ }
58
+
59
+ /** @type {SubItem[]} */
60
+ const subItems = [];
61
+ for (const backendNodeId of culprits.unsizedImages) {
62
+ subItems.push({
63
+ extra: makeNodeItemForNodeId(TraceElements, backendNodeId),
64
+ cause: insightStr_(InsightUIStrings.unsizedImages),
65
+ });
66
+ }
67
+ for (const request of culprits.fontRequests) {
68
+ const url = request.args.data.url;
69
+ subItems.push({
70
+ extra: {type: 'url', value: url},
71
+ cause: insightStr_(InsightUIStrings.fontRequest),
72
+ });
73
+ }
74
+ if (culprits.iframeIds.length) {
75
+ subItems.push({
76
+ cause: insightStr_(InsightUIStrings.injectedIframe),
77
+ });
78
+ }
79
+
80
+ if (subItems.length) {
81
+ return {type: 'subitems', items: subItems};
82
+ }
83
+ }
84
+
33
85
  /**
34
86
  * @param {LH.Artifacts} artifacts
35
87
  * @param {LH.Audit.Context} context
36
88
  * @return {Promise<LH.Audit.Product>}
37
89
  */
38
90
  static async audit(artifacts, context) {
39
- // TODO: implement.
40
91
  return adaptInsightToAuditProduct(artifacts, context, 'CLSCulprits', (insight) => {
41
92
  /** @type {LH.Audit.Details.Table['headings']} */
42
93
  const headings = [
94
+ /* eslint-disable max-len */
95
+ {key: 'node', valueType: 'node', subItemsHeading: {key: 'extra'}, label: insightStr_(i18n.UIStrings.columnElement)},
96
+ {key: 'score', valueType: 'numeric', subItemsHeading: {key: 'cause', valueType: 'text'}, granularity: 0.001, label: str_(UIStrings.columnScore)},
97
+ /* eslint-enable max-len */
43
98
  ];
44
- /** @type {LH.Audit.Details.Table['items']} */
45
- const items = [
46
- ];
47
- return Audit.makeTableDetails(headings, items);
99
+
100
+ const tables = insight.clusters.map(cluster => {
101
+ const events =
102
+ /** @type {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */ (
103
+ cluster.events.filter(e => !!e.args.data)
104
+ ).sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
105
+ .slice(0, MAX_LAYOUT_SHIFTS_PER_CLUSTER);
106
+ const impactByNodeId = CumulativeLayoutShift.getImpactByNodeId(events.map(e => ({
107
+ impactedNodes: e.args.data.impacted_nodes,
108
+ ts: e.ts,
109
+ isMainFrame: e.args.data.is_main_frame,
110
+ weightedScore: e.args.data.weighted_score_delta,
111
+ event: /** @type {any} */ (e),
112
+ })));
113
+
114
+ /** @type {LH.Audit.Details.Table['items']} */
115
+ const items = events.map(event => {
116
+ const biggestImpactNodeId = TraceElements.getBiggestImpactNodeForShiftEvent(
117
+ event.args.data.impacted_nodes || [], impactByNodeId, event);
118
+ return {
119
+ node: makeNodeItemForNodeId(artifacts.TraceElements, biggestImpactNodeId),
120
+ score: event.args.data?.weighted_score_delta,
121
+ subItems: this.getCulpritSubItems(insight, event, artifacts.TraceElements),
122
+ };
123
+ });
124
+ items.unshift({
125
+ node: {type: 'text', value: insightStr_(i18n.UIStrings.total)},
126
+ score: cluster.clusterCumulativeScore,
127
+ });
128
+ return Audit.makeTableDetails(headings, items);
129
+ });
130
+
131
+ return Audit.makeListDetails(tables);
48
132
  });
49
133
  }
50
134
  }
51
135
 
52
136
  export default CLSCulpritsInsight;
137
+ export {UIStrings};
@@ -25,6 +25,7 @@ class DocumentLatencyInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['redirects', 'server-response-time', 'uses-text-compression'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class DOMSizeInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['dom-size'],
28
29
  };
29
30
  }
30
31
 
@@ -27,6 +27,7 @@ class FontDisplayInsight extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  guidanceLevel: 3,
29
29
  requiredArtifacts: ['traces', 'TraceElements'],
30
+ replacesAudits: ['font-display'],
30
31
  };
31
32
  }
32
33
 
@@ -25,6 +25,12 @@ class ImageDeliveryInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: [
29
+ 'modern-image-formats',
30
+ 'uses-optimized-images',
31
+ 'efficient-animated-content',
32
+ 'uses-responsive-images',
33
+ ],
28
34
  };
29
35
  }
30
36
 
@@ -25,6 +25,7 @@ class InteractionToNextPaintInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['work-during-interaction'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class LCPDiscoveryInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['prioritize-lcp-image', 'lcp-lazy-loaded'],
28
29
  };
29
30
  }
30
31
 
@@ -25,6 +25,7 @@ class LCPPhasesInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['largest-contentful-paint-element'],
28
29
  };
29
30
  }
30
31
 
@@ -27,6 +27,7 @@ class LongCriticalNetworkTreeInsight extends Audit {
27
27
  description: str_(UIStrings.description),
28
28
  guidanceLevel: 3,
29
29
  requiredArtifacts: ['traces', 'TraceElements'],
30
+ replacesAudits: ['critical-request-chains'],
30
31
  };
31
32
  }
32
33
 
@@ -25,6 +25,7 @@ class RenderBlockingInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['render-blocking-resources'],
28
29
  };
29
30
  }
30
31
 
@@ -32,6 +32,7 @@ class ThirdPartiesInsight extends Audit {
32
32
  description: str_(UIStrings.description),
33
33
  guidanceLevel: 3,
34
34
  requiredArtifacts: ['traces', 'TraceElements'],
35
+ replacesAudits: ['third-party-summary'],
35
36
  };
36
37
  }
37
38
 
@@ -25,6 +25,7 @@ class ViewportInsight extends Audit {
25
25
  description: str_(UIStrings.description),
26
26
  guidanceLevel: 3,
27
27
  requiredArtifacts: ['traces', 'TraceElements'],
28
+ replacesAudits: ['viewport'],
28
29
  };
29
30
  }
30
31
 
@@ -79,7 +79,7 @@ class LayoutShifts extends Audit {
79
79
  const items = [];
80
80
  const layoutShiftEvents =
81
81
  /** @type {import('../lib/trace-engine.js').SaneSyntheticLayoutShift[]} */(
82
- clusters.flatMap(c => c.events)
82
+ clusters.flatMap(c => c.events).filter(e => !!e.args.data)
83
83
  );
84
84
  const topLayoutShiftEvents = layoutShiftEvents
85
85
  .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
@@ -1,6 +1,6 @@
1
- /** @type {Record<string, {name: LH.IcuMessage} | undefined>} */
1
+ /** @type {Record<string, {name: LH.IcuMessage|string} | undefined>} */
2
2
  export const NotRestoredReasonDescription: Record<string, {
3
- name: LH.IcuMessage;
3
+ name: LH.IcuMessage | string;
4
4
  } | undefined>;
5
5
  export namespace UIStrings {
6
6
  let notMainFrame: string;
@@ -77,7 +77,6 @@ export namespace UIStrings {
77
77
  let printing: string;
78
78
  let webDatabase: string;
79
79
  let pictureInPicture: string;
80
- let portal: string;
81
80
  let speechRecognizer: string;
82
81
  let idleManager: string;
83
82
  let paymentManager: string;
@@ -87,6 +86,7 @@ export namespace UIStrings {
87
86
  let outstandingNetworkRequestDirectSocket: string;
88
87
  let injectedJavascript: string;
89
88
  let injectedStyleSheet: string;
89
+ let contentDiscarded: string;
90
90
  let contentSecurityHandler: string;
91
91
  let contentWebAuthenticationAPI: string;
92
92
  let contentFileChooser: string;
@@ -117,8 +117,11 @@ export namespace UIStrings {
117
117
  let errorDocument: string;
118
118
  let fencedFramesEmbedder: string;
119
119
  let keepaliveRequest: string;
120
- let authorizationHeader: string;
120
+ let jsNetworkRequestReceivedCacheControlNoStoreResource: string;
121
121
  let indexedDBEvent: string;
122
122
  let cookieDisabled: string;
123
+ let webRTCSticky: string;
124
+ let webTransportSticky: string;
125
+ let webSocketSticky: string;
123
126
  }
124
127
  //# sourceMappingURL=bf-cache-strings.d.ts.map