lighthouse 10.2.0-dev.20230517 → 10.2.0-dev.20230519
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.
- package/core/audits/layout-shift-elements.d.ts +3 -2
- package/core/audits/layout-shift-elements.js +10 -2
- package/core/audits/seo/is-crawlable.d.ts +0 -1
- package/core/audits/server-response-time.js +4 -4
- package/core/gather/driver/target-manager.d.ts +13 -0
- package/core/gather/driver/target-manager.js +49 -0
- package/core/gather/gatherers/global-listeners.js +38 -21
- package/core/legacy/gather/driver.d.ts +1 -0
- package/core/legacy/gather/driver.js +11 -0
- package/core/lib/dependency-graph/simulator/network-analyzer.d.ts +6 -2
- package/core/lib/dependency-graph/simulator/network-analyzer.js +25 -8
- package/package.json +1 -1
- package/readme.md +1 -0
- package/types/audit.d.ts +3 -0
- package/types/gatherer.d.ts +1 -0
- package/types/lhr/audit-details.d.ts +0 -9
- package/types/lhr/audit-result.d.ts +8 -2
|
@@ -2,9 +2,10 @@ export default LayoutShiftElements;
|
|
|
2
2
|
declare class LayoutShiftElements extends Audit {
|
|
3
3
|
/**
|
|
4
4
|
* @param {LH.Artifacts} artifacts
|
|
5
|
-
* @
|
|
5
|
+
* @param {LH.Audit.Context} context
|
|
6
|
+
* @return {Promise<LH.Audit.Product>}
|
|
6
7
|
*/
|
|
7
|
-
static audit(artifacts: LH.Artifacts): LH.Audit.Product
|
|
8
|
+
static audit(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<LH.Audit.Product>;
|
|
8
9
|
}
|
|
9
10
|
export namespace UIStrings {
|
|
10
11
|
const title: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {Audit} from './audit.js';
|
|
8
8
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
|
+
import {CumulativeLayoutShift} from '../computed/metrics/cumulative-layout-shift.js';
|
|
9
10
|
|
|
10
11
|
const UIStrings = {
|
|
11
12
|
/** Descriptive title of a diagnostic audit that provides up to the top five elements contributing to Cumulative Layout Shift. */
|
|
@@ -34,9 +35,10 @@ class LayoutShiftElements extends Audit {
|
|
|
34
35
|
|
|
35
36
|
/**
|
|
36
37
|
* @param {LH.Artifacts} artifacts
|
|
37
|
-
* @
|
|
38
|
+
* @param {LH.Audit.Context} context
|
|
39
|
+
* @return {Promise<LH.Audit.Product>}
|
|
38
40
|
*/
|
|
39
|
-
static audit(artifacts) {
|
|
41
|
+
static async audit(artifacts, context) {
|
|
40
42
|
const clsElements = artifacts.TraceElements
|
|
41
43
|
.filter(element => element.traceEventType === 'layout-shift');
|
|
42
44
|
|
|
@@ -61,8 +63,14 @@ class LayoutShiftElements extends Audit {
|
|
|
61
63
|
{nodeCount: clsElementData.length});
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
const {cumulativeLayoutShift: clsSavings} =
|
|
67
|
+
await CumulativeLayoutShift.request(artifacts.traces[Audit.DEFAULT_PASS], context);
|
|
68
|
+
|
|
64
69
|
return {
|
|
65
70
|
score: 1,
|
|
71
|
+
metricSavings: {
|
|
72
|
+
CLS: clsSavings,
|
|
73
|
+
},
|
|
66
74
|
notApplicable: details.items.length === 0,
|
|
67
75
|
displayValue,
|
|
68
76
|
details,
|
|
@@ -76,10 +76,6 @@ class ServerResponseTime extends Audit {
|
|
|
76
76
|
[{url: mainResource.url, responseTime}],
|
|
77
77
|
{overallSavingsMs}
|
|
78
78
|
);
|
|
79
|
-
details.metricSavings = {
|
|
80
|
-
FCP: overallSavingsMs,
|
|
81
|
-
LCP: overallSavingsMs,
|
|
82
|
-
};
|
|
83
79
|
|
|
84
80
|
return {
|
|
85
81
|
numericValue: responseTime,
|
|
@@ -87,6 +83,10 @@ class ServerResponseTime extends Audit {
|
|
|
87
83
|
score: Number(passed),
|
|
88
84
|
displayValue,
|
|
89
85
|
details,
|
|
86
|
+
metricSavings: {
|
|
87
|
+
FCP: overallSavingsMs,
|
|
88
|
+
LCP: overallSavingsMs,
|
|
89
|
+
},
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
}
|
|
@@ -18,12 +18,15 @@ export class TargetManager extends TargetManager_base {
|
|
|
18
18
|
constructor(cdpSession: LH.Puppeteer.CDPSession);
|
|
19
19
|
_enabled: boolean;
|
|
20
20
|
_rootCdpSession: import("../../../types/puppeteer.js").default.CDPSession;
|
|
21
|
+
_mainFrameId: string;
|
|
21
22
|
/**
|
|
22
23
|
* A map of target id to target/session information. Used to ensure unique
|
|
23
24
|
* attached targets.
|
|
24
25
|
* @type {Map<string, TargetWithSession>}
|
|
25
26
|
*/
|
|
26
27
|
_targetIdToTargets: Map<string, TargetWithSession>;
|
|
28
|
+
/** @type {Map<string, LH.Crdp.Runtime.ExecutionContextDescription>} */
|
|
29
|
+
_executionContextIdToDescriptions: Map<string, LH.Crdp.Runtime.ExecutionContextDescription>;
|
|
27
30
|
/**
|
|
28
31
|
* @param {LH.Puppeteer.CDPSession} cdpSession
|
|
29
32
|
*/
|
|
@@ -32,6 +35,15 @@ export class TargetManager extends TargetManager_base {
|
|
|
32
35
|
* @param {LH.Crdp.Page.FrameNavigatedEvent} frameNavigatedEvent
|
|
33
36
|
*/
|
|
34
37
|
_onFrameNavigated(frameNavigatedEvent: LH.Crdp.Page.FrameNavigatedEvent): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* @param {LH.Crdp.Runtime.ExecutionContextCreatedEvent} event
|
|
40
|
+
*/
|
|
41
|
+
_onExecutionContextCreated(event: LH.Crdp.Runtime.ExecutionContextCreatedEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* @param {LH.Crdp.Runtime.ExecutionContextDestroyedEvent} event
|
|
44
|
+
*/
|
|
45
|
+
_onExecutionContextDestroyed(event: LH.Crdp.Runtime.ExecutionContextDestroyedEvent): void;
|
|
46
|
+
_onExecutionContextsCleared(): void;
|
|
35
47
|
/**
|
|
36
48
|
* @param {string} sessionId
|
|
37
49
|
* @return {LH.Gatherer.FRProtocolSession}
|
|
@@ -42,6 +54,7 @@ export class TargetManager extends TargetManager_base {
|
|
|
42
54
|
* @return {LH.Gatherer.FRProtocolSession}
|
|
43
55
|
*/
|
|
44
56
|
rootSession(): LH.Gatherer.FRProtocolSession;
|
|
57
|
+
mainFrameExecutionContexts(): import("devtools-protocol").Protocol.Runtime.ExecutionContextDescription[];
|
|
45
58
|
/**
|
|
46
59
|
* Returns a listener for all protocol events from session, and augments the
|
|
47
60
|
* event with the sessionId.
|
|
@@ -40,6 +40,7 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
40
40
|
|
|
41
41
|
this._enabled = false;
|
|
42
42
|
this._rootCdpSession = cdpSession;
|
|
43
|
+
this._mainFrameId = '';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* A map of target id to target/session information. Used to ensure unique
|
|
@@ -47,9 +48,14 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
47
48
|
* @type {Map<string, TargetWithSession>}
|
|
48
49
|
*/
|
|
49
50
|
this._targetIdToTargets = new Map();
|
|
51
|
+
/** @type {Map<string, LH.Crdp.Runtime.ExecutionContextDescription>} */
|
|
52
|
+
this._executionContextIdToDescriptions = new Map();
|
|
50
53
|
|
|
51
54
|
this._onSessionAttached = this._onSessionAttached.bind(this);
|
|
52
55
|
this._onFrameNavigated = this._onFrameNavigated.bind(this);
|
|
56
|
+
this._onExecutionContextCreated = this._onExecutionContextCreated.bind(this);
|
|
57
|
+
this._onExecutionContextDestroyed = this._onExecutionContextDestroyed.bind(this);
|
|
58
|
+
this._onExecutionContextsCleared = this._onExecutionContextsCleared.bind(this);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
61
|
/**
|
|
@@ -97,6 +103,12 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
97
103
|
return this._findSession(rootSessionId);
|
|
98
104
|
}
|
|
99
105
|
|
|
106
|
+
mainFrameExecutionContexts() {
|
|
107
|
+
return [...this._executionContextIdToDescriptions.values()].filter(executionContext => {
|
|
108
|
+
return executionContext.auxData.frameId === this._mainFrameId;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
/**
|
|
101
113
|
* @param {LH.Puppeteer.CDPSession} cdpSession
|
|
102
114
|
*/
|
|
@@ -153,6 +165,27 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
153
165
|
}
|
|
154
166
|
}
|
|
155
167
|
|
|
168
|
+
/**
|
|
169
|
+
* @param {LH.Crdp.Runtime.ExecutionContextCreatedEvent} event
|
|
170
|
+
*/
|
|
171
|
+
_onExecutionContextCreated(event) {
|
|
172
|
+
if (event.context.name === '__puppeteer_utility_world__') return;
|
|
173
|
+
if (event.context.name === 'lighthouse_isolated_context') return;
|
|
174
|
+
|
|
175
|
+
this._executionContextIdToDescriptions.set(event.context.uniqueId, event.context);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {LH.Crdp.Runtime.ExecutionContextDestroyedEvent} event
|
|
180
|
+
*/
|
|
181
|
+
_onExecutionContextDestroyed(event) {
|
|
182
|
+
this._executionContextIdToDescriptions.delete(event.executionContextUniqueId);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
_onExecutionContextsCleared() {
|
|
186
|
+
this._executionContextIdToDescriptions.clear();
|
|
187
|
+
}
|
|
188
|
+
|
|
156
189
|
/**
|
|
157
190
|
* Returns a listener for all protocol events from session, and augments the
|
|
158
191
|
* event with the sessionId.
|
|
@@ -183,10 +216,17 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
183
216
|
|
|
184
217
|
this._enabled = true;
|
|
185
218
|
this._targetIdToTargets = new Map();
|
|
219
|
+
this._executionContextIdToDescriptions = new Map();
|
|
186
220
|
|
|
187
221
|
this._rootCdpSession.on('Page.frameNavigated', this._onFrameNavigated);
|
|
222
|
+
this._rootCdpSession.on('Runtime.executionContextCreated', this._onExecutionContextCreated);
|
|
223
|
+
this._rootCdpSession.on('Runtime.executionContextDestroyed', this._onExecutionContextDestroyed);
|
|
224
|
+
this._rootCdpSession.on('Runtime.executionContextsCleared', this._onExecutionContextsCleared);
|
|
188
225
|
|
|
189
226
|
await this._rootCdpSession.send('Page.enable');
|
|
227
|
+
await this._rootCdpSession.send('Runtime.enable');
|
|
228
|
+
|
|
229
|
+
this._mainFrameId = (await this._rootCdpSession.send('Page.getFrameTree')).frameTree.frame.id;
|
|
190
230
|
|
|
191
231
|
// Start with the already attached root session.
|
|
192
232
|
await this._onSessionAttached(this._rootCdpSession);
|
|
@@ -197,14 +237,23 @@ class TargetManager extends ProtocolEventEmitter {
|
|
|
197
237
|
*/
|
|
198
238
|
async disable() {
|
|
199
239
|
this._rootCdpSession.off('Page.frameNavigated', this._onFrameNavigated);
|
|
240
|
+
this._rootCdpSession.off('Runtime.executionContextCreated', this._onExecutionContextCreated);
|
|
241
|
+
this._rootCdpSession.off('Runtime.executionContextDestroyed',
|
|
242
|
+
this._onExecutionContextDestroyed);
|
|
243
|
+
this._rootCdpSession.off('Runtime.executionContextsCleared', this._onExecutionContextsCleared);
|
|
200
244
|
|
|
201
245
|
for (const {cdpSession, protocolListener} of this._targetIdToTargets.values()) {
|
|
202
246
|
cdpSession.off('*', protocolListener);
|
|
203
247
|
cdpSession.off('sessionattached', this._onSessionAttached);
|
|
204
248
|
}
|
|
205
249
|
|
|
250
|
+
await this._rootCdpSession.send('Page.disable');
|
|
251
|
+
await this._rootCdpSession.send('Runtime.disable');
|
|
252
|
+
|
|
206
253
|
this._enabled = false;
|
|
207
254
|
this._targetIdToTargets = new Map();
|
|
255
|
+
this._executionContextIdToDescriptions = new Map();
|
|
256
|
+
this._mainFrameId = '';
|
|
208
257
|
}
|
|
209
258
|
}
|
|
210
259
|
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
* around page unload, but this can be expanded in the future.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import log from 'lighthouse-logger';
|
|
15
|
+
|
|
14
16
|
import FRGatherer from '../base-gatherer.js';
|
|
15
17
|
|
|
16
18
|
class GlobalListeners extends FRGatherer {
|
|
@@ -61,30 +63,45 @@ class GlobalListeners extends FRGatherer {
|
|
|
61
63
|
async getArtifact(passContext) {
|
|
62
64
|
const session = passContext.driver.defaultSession;
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
expression: 'window',
|
|
67
|
-
returnByValue: false,
|
|
68
|
-
});
|
|
69
|
-
if (!objectId) {
|
|
70
|
-
throw new Error('Error fetching information about the global object');
|
|
71
|
-
}
|
|
66
|
+
/** @type {Array<LH.Artifacts.GlobalListener>} */
|
|
67
|
+
const listeners = [];
|
|
72
68
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
for (const executionContext of passContext.driver.targetManager.mainFrameExecutionContexts()) {
|
|
70
|
+
// Get a RemoteObject handle to `window`.
|
|
71
|
+
let objectId;
|
|
72
|
+
try {
|
|
73
|
+
const {result} = await session.sendCommand('Runtime.evaluate', {
|
|
74
|
+
expression: 'window',
|
|
75
|
+
returnByValue: false,
|
|
76
|
+
uniqueContextId: executionContext.uniqueId,
|
|
77
|
+
});
|
|
78
|
+
if (!result.objectId) {
|
|
79
|
+
throw new Error('Error fetching information about the global object');
|
|
80
|
+
}
|
|
81
|
+
objectId = result.objectId;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
// Execution context is no longer valid, but don't let that fail the gatherer.
|
|
84
|
+
log.warn('Execution context is no longer valid', executionContext, err);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// And get all its listeners of interest.
|
|
89
|
+
const response = await session.sendCommand('DOMDebugger.getEventListeners', {objectId});
|
|
90
|
+
for (const listener of response.listeners) {
|
|
91
|
+
if (GlobalListeners._filterForAllowlistedTypes(listener)) {
|
|
92
|
+
const {type, scriptId, lineNumber, columnNumber} = listener;
|
|
93
|
+
listeners.push({
|
|
94
|
+
type,
|
|
95
|
+
scriptId,
|
|
96
|
+
lineNumber,
|
|
97
|
+
columnNumber,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
85
102
|
|
|
86
103
|
// Dedupe listeners with same underlying data.
|
|
87
|
-
return this.dedupeListeners(
|
|
104
|
+
return this.dedupeListeners(listeners);
|
|
88
105
|
}
|
|
89
106
|
}
|
|
90
107
|
|
|
@@ -43,6 +43,7 @@ export class Driver implements LH.Gatherer.FRTransitionalDriver {
|
|
|
43
43
|
private evaluateAsync;
|
|
44
44
|
targetManager: {
|
|
45
45
|
rootSession: () => Driver;
|
|
46
|
+
mainFrameExecutionContexts: () => LH.Crdp.Runtime.ExecutionContextDescription[];
|
|
46
47
|
/**
|
|
47
48
|
* Bind to *any* protocol event.
|
|
48
49
|
* @param {'protocolevent'} event
|
|
@@ -101,6 +101,17 @@ class Driver {
|
|
|
101
101
|
rootSession: () => {
|
|
102
102
|
return this.defaultSession;
|
|
103
103
|
},
|
|
104
|
+
// For legacy driver, only bother supporting access to the default execution context.
|
|
105
|
+
mainFrameExecutionContexts: () => {
|
|
106
|
+
// @ts-expect-error - undefined ids are OK for purposes of calling protocol commands like Runtime.evaluate.
|
|
107
|
+
return [/** @type {LH.Crdp.Runtime.ExecutionContextDescription} */({
|
|
108
|
+
id: undefined,
|
|
109
|
+
uniqueId: undefined,
|
|
110
|
+
origin: '',
|
|
111
|
+
name: '',
|
|
112
|
+
auxData: {isDefault: true, type: 'default', frameId: ''},
|
|
113
|
+
})];
|
|
114
|
+
},
|
|
104
115
|
/**
|
|
105
116
|
* Bind to *any* protocol event.
|
|
106
117
|
* @param {'protocolevent'} event
|
|
@@ -58,13 +58,17 @@ export class NetworkAnalyzer {
|
|
|
58
58
|
connectionReused?: boolean | undefined;
|
|
59
59
|
}) => number | number[] | undefined): Map<string, number[]>;
|
|
60
60
|
/**
|
|
61
|
-
* Estimates the observed RTT to each origin based on how long the
|
|
61
|
+
* Estimates the observed RTT to each origin based on how long the connection setup.
|
|
62
|
+
* For h1 and h2, this could includes two estimates - one for the TCP handshake, another for
|
|
63
|
+
* SSL negotiation.
|
|
64
|
+
* For h3, we get only one estimate since QUIC establishes a secure connection in a
|
|
65
|
+
* single handshake.
|
|
62
66
|
* This is the most accurate and preferred method of measurement when the data is available.
|
|
63
67
|
*
|
|
64
68
|
* @param {LH.Artifacts.NetworkRequest[]} records
|
|
65
69
|
* @return {Map<string, number[]>}
|
|
66
70
|
*/
|
|
67
|
-
static
|
|
71
|
+
static _estimateRTTByOriginViaConnectionTiming(records: LH.Artifacts.NetworkRequest[]): Map<string, number[]>;
|
|
68
72
|
/**
|
|
69
73
|
* Estimates the observed RTT to each origin based on how long a download took on a fresh connection.
|
|
70
74
|
* NOTE: this will tend to overestimate the actual RTT quite significantly as the download can be
|
|
@@ -52,11 +52,22 @@ class NetworkAnalyzer {
|
|
|
52
52
|
static getSummary(values) {
|
|
53
53
|
values.sort((a, b) => a - b);
|
|
54
54
|
|
|
55
|
+
let median;
|
|
56
|
+
if (values.length === 0) {
|
|
57
|
+
median = values[0];
|
|
58
|
+
} else if (values.length % 2 === 0) {
|
|
59
|
+
const a = values[Math.floor((values.length - 1) / 2)];
|
|
60
|
+
const b = values[Math.floor((values.length - 1) / 2) + 1];
|
|
61
|
+
median = (a + b) / 2;
|
|
62
|
+
} else {
|
|
63
|
+
median = values[Math.floor((values.length - 1) / 2)];
|
|
64
|
+
}
|
|
65
|
+
|
|
55
66
|
return {
|
|
56
67
|
min: values[0],
|
|
57
68
|
max: values[values.length - 1],
|
|
58
69
|
avg: values.reduce((a, b) => a + b, 0) / values.length,
|
|
59
|
-
median
|
|
70
|
+
median,
|
|
60
71
|
};
|
|
61
72
|
}
|
|
62
73
|
|
|
@@ -114,19 +125,25 @@ class NetworkAnalyzer {
|
|
|
114
125
|
}
|
|
115
126
|
|
|
116
127
|
/**
|
|
117
|
-
* Estimates the observed RTT to each origin based on how long the
|
|
128
|
+
* Estimates the observed RTT to each origin based on how long the connection setup.
|
|
129
|
+
* For h1 and h2, this could includes two estimates - one for the TCP handshake, another for
|
|
130
|
+
* SSL negotiation.
|
|
131
|
+
* For h3, we get only one estimate since QUIC establishes a secure connection in a
|
|
132
|
+
* single handshake.
|
|
118
133
|
* This is the most accurate and preferred method of measurement when the data is available.
|
|
119
134
|
*
|
|
120
135
|
* @param {LH.Artifacts.NetworkRequest[]} records
|
|
121
136
|
* @return {Map<string, number[]>}
|
|
122
137
|
*/
|
|
123
|
-
static
|
|
124
|
-
return NetworkAnalyzer._estimateValueByOrigin(records, ({timing, connectionReused}) => {
|
|
138
|
+
static _estimateRTTByOriginViaConnectionTiming(records) {
|
|
139
|
+
return NetworkAnalyzer._estimateValueByOrigin(records, ({timing, connectionReused, record}) => {
|
|
125
140
|
if (connectionReused) return;
|
|
126
141
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
142
|
+
if (timing.connectEnd > 0 && timing.connectStart > 0 && record.protocol.startsWith('h3')) {
|
|
143
|
+
// These values are equal to sslStart and sslEnd for h3.
|
|
144
|
+
return timing.connectEnd - timing.connectStart;
|
|
145
|
+
} else if (timing.sslStart > 0 && timing.sslEnd > 0) {
|
|
146
|
+
// SSL can also be more than 1 RT but assume False Start was used.
|
|
130
147
|
return [timing.connectEnd - timing.sslStart, timing.sslStart - timing.connectStart];
|
|
131
148
|
} else if (timing.connectStart > 0 && timing.connectEnd > 0) {
|
|
132
149
|
return timing.connectEnd - timing.connectStart;
|
|
@@ -318,7 +335,7 @@ class NetworkAnalyzer {
|
|
|
318
335
|
useHeadersEndEstimates = true,
|
|
319
336
|
} = options || {};
|
|
320
337
|
|
|
321
|
-
let estimatesByOrigin = NetworkAnalyzer.
|
|
338
|
+
let estimatesByOrigin = NetworkAnalyzer._estimateRTTByOriginViaConnectionTiming(records);
|
|
322
339
|
if (!estimatesByOrigin.size || forceCoarseEstimates) {
|
|
323
340
|
estimatesByOrigin = new Map();
|
|
324
341
|
const estimatesViaDownload = NetworkAnalyzer._estimateRTTByOriginViaDownloadTiming(records);
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -399,6 +399,7 @@ Other awesome open source projects that use Lighthouse.
|
|
|
399
399
|
* **[site-audit-seo](https://github.com/viasite/site-audit-seo)** - CLI tool for SEO site audit, crawl site, lighthouse each page. Output to console and tables in csv, xlsx, json, web or Google Drive.
|
|
400
400
|
* **[webpack-lighthouse-plugin](https://github.com/addyosmani/webpack-lighthouse-plugin)** - Run Lighthouse from a Webpack build.
|
|
401
401
|
* **[cypress-audit](https://github.com/mfrachet/cypress-audit)** - Run Lighthouse and Pa11y audits directly in your E2E test suites.
|
|
402
|
+
* **[laravel-lighthouse](https://github.com/adityadees/laravel-lighthouse)** - Google Lighthouse wrapper for laravel framework to run Google Lighthouse CLI with custom option and can automatically save result in your server directory.
|
|
402
403
|
|
|
403
404
|
## FAQ
|
|
404
405
|
|
package/types/audit.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ declare module Audit {
|
|
|
18
18
|
export type Result = AuditResult.Result;
|
|
19
19
|
export type ScoreDisplayMode = AuditResult.ScoreDisplayMode;
|
|
20
20
|
export type ScoreDisplayModes = AuditResult.ScoreDisplayModes;
|
|
21
|
+
export type MetricSavings = AuditResult.MetricSavings;
|
|
21
22
|
|
|
22
23
|
type Context = Util.Immutable<{
|
|
23
24
|
/** audit options */
|
|
@@ -81,6 +82,8 @@ declare module Audit {
|
|
|
81
82
|
details?: AuditDetails;
|
|
82
83
|
/** If an audit encounters unusual execution circumstances, strings can be put in this optional array to add top-level warnings to the LHR. */
|
|
83
84
|
runWarnings?: Array<IcuMessage>;
|
|
85
|
+
/** [EXPERIMENTAL] Estimates of how much this audit affects various performance metrics. Values will be in the unit of the respective metrics. */
|
|
86
|
+
metricSavings?: MetricSavings;
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
/** The Audit.Product type for audits that do not return a `numericValue`. */
|
package/types/gatherer.d.ts
CHANGED
|
@@ -47,6 +47,7 @@ declare module Gatherer {
|
|
|
47
47
|
url: () => Promise<string>;
|
|
48
48
|
targetManager: {
|
|
49
49
|
rootSession(): FRProtocolSession;
|
|
50
|
+
mainFrameExecutionContexts(): Array<Crdp.Runtime.ExecutionContextDescription>;
|
|
50
51
|
on(event: 'protocolevent', callback: (payload: Protocol.RawEventMessage) => void): void
|
|
51
52
|
off(event: 'protocolevent', callback: (payload: Protocol.RawEventMessage) => void): void
|
|
52
53
|
};
|
|
@@ -15,8 +15,6 @@ interface BaseDetails {
|
|
|
15
15
|
overallSavingsBytes?: number;
|
|
16
16
|
/** Additional information, usually used for including debug or meta information in the LHR */
|
|
17
17
|
debugData?: Details.DebugData;
|
|
18
|
-
/** Estimates of how much this audit affects various performance metrics. Values will be in the unit of the respective metrics. */
|
|
19
|
-
metricSavings?: Details.MetricSavings;
|
|
20
18
|
}
|
|
21
19
|
|
|
22
20
|
type Details =
|
|
@@ -306,13 +304,6 @@ declare module Details {
|
|
|
306
304
|
granularity?: number,
|
|
307
305
|
}
|
|
308
306
|
|
|
309
|
-
interface MetricSavings {
|
|
310
|
-
LCP?: number;
|
|
311
|
-
FCP?: number;
|
|
312
|
-
CLS?: number;
|
|
313
|
-
TBT?: number;
|
|
314
|
-
INP?: number;
|
|
315
|
-
}
|
|
316
307
|
}
|
|
317
308
|
|
|
318
309
|
export default Details;
|
|
@@ -24,6 +24,14 @@ interface ScoreDisplayModes {
|
|
|
24
24
|
|
|
25
25
|
type ScoreDisplayMode = ScoreDisplayModes[keyof ScoreDisplayModes];
|
|
26
26
|
|
|
27
|
+
interface MetricSavings {
|
|
28
|
+
LCP?: number;
|
|
29
|
+
FCP?: number;
|
|
30
|
+
CLS?: number;
|
|
31
|
+
TBT?: number;
|
|
32
|
+
INP?: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
/** Audit result returned in Lighthouse report. All audits offer a description and score of 0-1. */
|
|
28
36
|
export interface Result {
|
|
29
37
|
displayValue?: string;
|
|
@@ -50,8 +58,6 @@ export interface Result {
|
|
|
50
58
|
id: string;
|
|
51
59
|
/** A more detailed description that describes why the audit is important and links to Lighthouse documentation on the audit; markdown links supported. */
|
|
52
60
|
description: string;
|
|
53
|
-
/** Estimates of how much this audit affects various performance metrics. Values will be in the unit of the respective metrics. */
|
|
54
|
-
metricSavings?: AuditDetails.MetricSavings;
|
|
55
61
|
/** A numeric value that has a meaning specific to the audit, e.g. the number of nodes in the DOM or the timestamp of a specific load event. More information can be found in the audit details, if present. */
|
|
56
62
|
numericValue?: number;
|
|
57
63
|
/** The unit of `numericValue`, used when the consumer wishes to convert numericValue to a display string. */
|