lighthouse 10.0.1-dev.20230216 → 10.0.1-dev.20230217

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.
@@ -1,29 +1,37 @@
1
1
  export default PrioritizeLcpImage;
2
+ export type InitiatorType = LH.Crdp.Network.Initiator['type'] | 'redirect' | 'fallbackToMain';
2
3
  export type InitiatorPath = Array<{
3
4
  url: string;
4
- initiatorType: string;
5
+ initiatorType: InitiatorType;
5
6
  }>;
6
7
  /**
7
- * @typedef {Array<{url: string, initiatorType: string}>} InitiatorPath
8
+ * @typedef {LH.Crdp.Network.Initiator['type']|'redirect'|'fallbackToMain'} InitiatorType
9
+ * @typedef {Array<{url: string, initiatorType: InitiatorType}>} InitiatorPath
8
10
  */
9
11
  declare class PrioritizeLcpImage extends Audit {
10
12
  /**
11
13
  *
12
14
  * @param {LH.Artifacts.NetworkRequest} request
13
15
  * @param {LH.Artifacts.NetworkRequest} mainResource
14
- * @param {Array<LH.Gatherer.Simulation.GraphNode>} initiatorPath
16
+ * @param {InitiatorPath} initiatorPath
15
17
  * @return {boolean}
16
18
  */
17
- static shouldPreloadRequest(request: LH.Artifacts.NetworkRequest, mainResource: LH.Artifacts.NetworkRequest, initiatorPath: Array<LH.Gatherer.Simulation.GraphNode>): boolean;
19
+ static shouldPreloadRequest(request: LH.Artifacts.NetworkRequest, mainResource: LH.Artifacts.NetworkRequest, initiatorPath: InitiatorPath): boolean;
18
20
  /**
19
21
  * @param {LH.Gatherer.Simulation.GraphNode} graph
20
22
  * @param {NetworkRequest} lcpRecord
21
- * @return {{lcpNode: LH.Gatherer.Simulation.GraphNetworkNode|undefined, path: Array<LH.Gatherer.Simulation.GraphNetworkNode>|undefined}}
23
+ * @return {LH.Gatherer.Simulation.GraphNetworkNode|undefined}
22
24
  */
23
- static findLCPNode(graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest): {
24
- lcpNode: LH.Gatherer.Simulation.GraphNetworkNode | undefined;
25
- path: Array<LH.Gatherer.Simulation.GraphNetworkNode> | undefined;
26
- };
25
+ static findLCPNode(graph: LH.Gatherer.Simulation.GraphNode, lcpRecord: NetworkRequest): LH.Gatherer.Simulation.GraphNetworkNode | undefined;
26
+ /**
27
+ * Get the initiator path starting with lcpRecord back to mainResource, inclusive.
28
+ * Navigation redirects *to* the mainResource are not included.
29
+ * Path returned will always be at least [lcpRecord, mainResource].
30
+ * @param {NetworkRequest} lcpRecord
31
+ * @param {NetworkRequest} mainResource
32
+ * @return {InitiatorPath}
33
+ */
34
+ static getLcpInitiatorPath(lcpRecord: NetworkRequest, mainResource: NetworkRequest): InitiatorPath;
27
35
  /**
28
36
  * @param {LH.Artifacts.NetworkRequest} mainResource
29
37
  * @param {LH.Gatherer.Simulation.GraphNode} graph
@@ -25,7 +25,8 @@ const UIStrings = {
25
25
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
26
26
 
27
27
  /**
28
- * @typedef {Array<{url: string, initiatorType: string}>} InitiatorPath
28
+ * @typedef {LH.Crdp.Network.Initiator['type']|'redirect'|'fallbackToMain'} InitiatorType
29
+ * @typedef {Array<{url: string, initiatorType: InitiatorType}>} InitiatorPath
29
30
  */
30
31
 
31
32
  class PrioritizeLcpImage extends Audit {
@@ -47,18 +48,16 @@ class PrioritizeLcpImage extends Audit {
47
48
  *
48
49
  * @param {LH.Artifacts.NetworkRequest} request
49
50
  * @param {LH.Artifacts.NetworkRequest} mainResource
50
- * @param {Array<LH.Gatherer.Simulation.GraphNode>} initiatorPath
51
+ * @param {InitiatorPath} initiatorPath
51
52
  * @return {boolean}
52
53
  */
53
54
  static shouldPreloadRequest(request, mainResource, initiatorPath) {
54
- const mainResourceDepth = mainResource.redirects ? mainResource.redirects.length : 0;
55
-
56
55
  // If it's already preloaded, no need to recommend it.
57
56
  if (request.isLinkPreload) return false;
58
57
  // It's not a request loaded over the network, don't recommend it.
59
58
  if (NetworkRequest.isNonNetworkRequest(request)) return false;
60
- // It's already discoverable from the main document, don't recommend it.
61
- if (initiatorPath.length <= mainResourceDepth) return false;
59
+ // It's already discoverable from the main document (a path of [lcpRecord, mainResource]), don't recommend it.
60
+ if (initiatorPath.length <= 2) return false;
62
61
  // Finally, return whether or not it belongs to the main frame
63
62
  return request.frameId === mainResource.frameId;
64
63
  }
@@ -66,23 +65,57 @@ class PrioritizeLcpImage extends Audit {
66
65
  /**
67
66
  * @param {LH.Gatherer.Simulation.GraphNode} graph
68
67
  * @param {NetworkRequest} lcpRecord
69
- * @return {{lcpNode: LH.Gatherer.Simulation.GraphNetworkNode|undefined, path: Array<LH.Gatherer.Simulation.GraphNetworkNode>|undefined}}
68
+ * @return {LH.Gatherer.Simulation.GraphNetworkNode|undefined}
70
69
  */
71
70
  static findLCPNode(graph, lcpRecord) {
72
- let lcpNode;
73
- let path;
74
- graph.traverse((node, traversalPath) => {
75
- if (node.type !== 'network') return;
71
+ for (const {node} of graph.traverseGenerator()) {
72
+ if (node.type !== 'network') continue;
76
73
  if (node.record.requestId === lcpRecord.requestId) {
77
- lcpNode = node;
78
- path =
79
- traversalPath.slice(1).filter(initiator => initiator.type === 'network');
74
+ return node;
80
75
  }
81
- });
82
- return {
83
- lcpNode,
84
- path,
85
- };
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Get the initiator path starting with lcpRecord back to mainResource, inclusive.
81
+ * Navigation redirects *to* the mainResource are not included.
82
+ * Path returned will always be at least [lcpRecord, mainResource].
83
+ * @param {NetworkRequest} lcpRecord
84
+ * @param {NetworkRequest} mainResource
85
+ * @return {InitiatorPath}
86
+ */
87
+ static getLcpInitiatorPath(lcpRecord, mainResource) {
88
+ /** @type {InitiatorPath} */
89
+ const initiatorPath = [];
90
+ let mainResourceReached = false;
91
+ /** @type {NetworkRequest|undefined} */
92
+ let request = lcpRecord;
93
+
94
+ while (request) {
95
+ mainResourceReached ||= request.requestId === mainResource.requestId;
96
+
97
+ /** @type {InitiatorType} */
98
+ let initiatorType = request.initiator?.type ?? 'other';
99
+ // Initiator type usually comes from redirect, but 'redirect' is used for more informative debugData.
100
+ if (request.initiatorRequest && request.initiatorRequest === request.redirectSource) {
101
+ initiatorType = 'redirect';
102
+ }
103
+ // Sometimes the initiator chain is broken and the best that can be done is stitch
104
+ // back to the main resource. Note this in the initiatorType.
105
+ if (!request.initiatorRequest && !mainResourceReached) {
106
+ initiatorType = 'fallbackToMain';
107
+ }
108
+
109
+ initiatorPath.push({url: request.url, initiatorType});
110
+
111
+ // Can't preload before the main resource, so break off initiator path there.
112
+ if (mainResourceReached) break;
113
+
114
+ // Continue up chain, falling back to mainResource if chain is broken.
115
+ request = request.initiatorRequest || mainResource;
116
+ }
117
+
118
+ return initiatorPath;
86
119
  }
87
120
 
88
121
  /**
@@ -93,18 +126,14 @@ class PrioritizeLcpImage extends Audit {
93
126
  */
94
127
  static getLCPNodeToPreload(mainResource, graph, lcpRecord) {
95
128
  if (!lcpRecord) return {};
96
- const {lcpNode, path} = PrioritizeLcpImage.findLCPNode(graph, lcpRecord);
97
- if (!lcpNode || !path) return {};
129
+ const lcpNode = PrioritizeLcpImage.findLCPNode(graph, lcpRecord);
130
+ const initiatorPath = PrioritizeLcpImage.getLcpInitiatorPath(lcpRecord, mainResource);
131
+ if (!lcpNode) return {initiatorPath};
98
132
 
99
133
  // eslint-disable-next-line max-len
100
- const shouldPreload = PrioritizeLcpImage.shouldPreloadRequest(lcpNode.record, mainResource, path);
134
+ const shouldPreload = PrioritizeLcpImage.shouldPreloadRequest(lcpRecord, mainResource, initiatorPath);
101
135
  const lcpNodeToPreload = shouldPreload ? lcpNode : undefined;
102
136
 
103
- const initiatorPath = [
104
- {url: lcpNode.record.url, initiatorType: lcpNode.initiatorType},
105
- ...path.map(n => ({url: n.record.url, initiatorType: n.initiatorType})),
106
- ];
107
-
108
137
  return {
109
138
  lcpNodeToPreload,
110
139
  initiatorPath,
@@ -276,6 +305,7 @@ class PrioritizeLcpImage extends Audit {
276
305
 
277
306
  const lcpRecord = PrioritizeLcpImage.getLcpRecord(trace, processedNavigation, networkRecords);
278
307
  const graph = lanternLCP.pessimisticGraph;
308
+ // Note: if moving to LCPAllFrames, mainResource would need to be the LCP frame's main resource.
279
309
  const {lcpNodeToPreload, initiatorPath} = PrioritizeLcpImage.getLCPNodeToPreload(mainResource,
280
310
  graph, lcpRecord);
281
311
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "10.0.1-dev.20230216",
4
+ "version": "10.0.1-dev.20230217",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {