lighthouse 12.0.0-dev.20240605 → 12.0.0-dev.20240607
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/metrics/first-meaningful-paint.d.ts +1 -17
- package/core/audits/metrics/first-meaningful-paint.js +5 -47
- package/core/audits/metrics/interactive.d.ts +1 -1
- package/core/audits/metrics/interactive.js +1 -1
- package/core/audits/predictive-perf.js +0 -6
- package/core/audits/redirects.js +1 -2
- package/core/computed/metrics/lantern-interactive.js +3 -3
- package/core/computed/metrics/lantern-metric.d.ts +2 -2
- package/core/computed/metrics/lantern-metric.js +4 -1
- package/core/computed/metrics/timing-summary.js +0 -6
- package/core/lib/lantern/lantern.d.ts +10 -5
- package/core/lib/lantern/lantern.js +47 -0
- package/core/lib/lantern/metric.d.ts +6 -6
- package/core/lib/lantern/metric.js +3 -3
- package/core/lib/lantern/metrics/first-contentful-paint.js +2 -2
- package/core/lib/lantern/metrics/interactive.js +7 -7
- package/core/lib/lantern/metrics/largest-contentful-paint.js +2 -2
- package/core/lib/lantern/types/lantern.d.ts +8 -1
- package/core/lib/tracehouse/trace-processor.d.ts +0 -2
- package/core/lib/tracehouse/trace-processor.js +1 -31
- package/core/lib/traces/metric-trace-events.js +0 -5
- package/dist/report/bundle.esm.js +1 -1
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +1 -1
- package/report/renderer/performance-category-renderer.js +1 -1
- package/types/artifacts.d.ts +0 -12
- package/core/computed/metrics/first-meaningful-paint.d.ts +0 -21
- package/core/computed/metrics/first-meaningful-paint.js +0 -44
- package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +0 -25
- package/core/computed/metrics/lantern-first-meaningful-paint.js +0 -43
- package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +0 -6
- package/core/lib/lantern/metrics/first-meaningful-paint.js +0 -64
|
@@ -1,25 +1,9 @@
|
|
|
1
1
|
export default FirstMeaningfulPaint;
|
|
2
2
|
declare class FirstMeaningfulPaint extends Audit {
|
|
3
3
|
/**
|
|
4
|
-
* @return {{mobile: {scoring: LH.Audit.ScoreOptions}, desktop: {scoring: LH.Audit.ScoreOptions}}}
|
|
5
|
-
*/
|
|
6
|
-
static get defaultOptions(): {
|
|
7
|
-
mobile: {
|
|
8
|
-
scoring: LH.Audit.ScoreOptions;
|
|
9
|
-
};
|
|
10
|
-
desktop: {
|
|
11
|
-
scoring: LH.Audit.ScoreOptions;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Audits the page to give a score for First Meaningful Paint.
|
|
16
|
-
* @see https://github.com/GoogleChrome/lighthouse/issues/26
|
|
17
|
-
* @see https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view
|
|
18
|
-
* @param {LH.Artifacts} artifacts The artifacts from the gather phase.
|
|
19
|
-
* @param {LH.Audit.Context} context
|
|
20
4
|
* @return {Promise<LH.Audit.Product>}
|
|
21
5
|
*/
|
|
22
|
-
static audit(
|
|
6
|
+
static audit(): Promise<LH.Audit.Product>;
|
|
23
7
|
}
|
|
24
8
|
export namespace UIStrings {
|
|
25
9
|
const description: string;
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
// TODO(COMPAT): This is just a shell. Remove in future breaking release.
|
|
8
|
+
|
|
7
9
|
import {Audit} from '../audit.js';
|
|
8
10
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
9
|
-
import {FirstMeaningfulPaint as ComputedFmp} from '../../computed/metrics/first-meaningful-paint.js';
|
|
10
11
|
|
|
11
12
|
const UIStrings = {
|
|
12
13
|
/** Description of the First Meaningful Paint (FMP) metric, which marks the time at which a majority of the content has been painted by the browser. This is displayed within a tooltip when the user hovers on the metric name to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
|
|
@@ -32,55 +33,12 @@ class FirstMeaningfulPaint extends Audit {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
|
-
* @return {{mobile: {scoring: LH.Audit.ScoreOptions}, desktop: {scoring: LH.Audit.ScoreOptions}}}
|
|
36
|
-
*/
|
|
37
|
-
static get defaultOptions() {
|
|
38
|
-
return {
|
|
39
|
-
mobile: {
|
|
40
|
-
// 25th and 5th percentiles HTTPArchive -> median and PODR, then p10 derived from them.
|
|
41
|
-
// https://bigquery.cloud.google.com/table/httparchive:lighthouse.2018_04_01_mobile?pli=1
|
|
42
|
-
// see https://www.desmos.com/calculator/i4znkdccut
|
|
43
|
-
scoring: {
|
|
44
|
-
p10: 2336,
|
|
45
|
-
median: 4000,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
desktop: {
|
|
49
|
-
// SELECT QUANTILES(renderStart, 21) FROM [httparchive:summary_pages.2018_12_15_desktop] LIMIT 1000
|
|
50
|
-
scoring: {
|
|
51
|
-
p10: 934,
|
|
52
|
-
median: 1600,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Audits the page to give a score for First Meaningful Paint.
|
|
60
|
-
* @see https://github.com/GoogleChrome/lighthouse/issues/26
|
|
61
|
-
* @see https://docs.google.com/document/d/1BR94tJdZLsin5poeet0XoTW60M0SjvOJQttKT-JK8HI/view
|
|
62
|
-
* @param {LH.Artifacts} artifacts The artifacts from the gather phase.
|
|
63
|
-
* @param {LH.Audit.Context} context
|
|
64
36
|
* @return {Promise<LH.Audit.Product>}
|
|
65
37
|
*/
|
|
66
|
-
static async audit(
|
|
67
|
-
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
|
68
|
-
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
|
69
|
-
const gatherContext = artifacts.GatherContext;
|
|
70
|
-
const metricComputationData = {trace, devtoolsLog, gatherContext,
|
|
71
|
-
settings: context.settings, URL: artifacts.URL};
|
|
72
|
-
const metricResult = await ComputedFmp.request(metricComputationData, context);
|
|
73
|
-
const options = context.options[context.settings.formFactor];
|
|
74
|
-
|
|
75
|
-
|
|
38
|
+
static async audit() {
|
|
76
39
|
return {
|
|
77
|
-
score:
|
|
78
|
-
|
|
79
|
-
metricResult.timing
|
|
80
|
-
),
|
|
81
|
-
numericValue: metricResult.timing,
|
|
82
|
-
numericUnit: 'millisecond',
|
|
83
|
-
displayValue: str_(i18n.UIStrings.seconds, {timeInMs: metricResult.timing}),
|
|
40
|
+
score: null,
|
|
41
|
+
notApplicable: true,
|
|
84
42
|
};
|
|
85
43
|
}
|
|
86
44
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default InteractiveMetric;
|
|
2
2
|
/**
|
|
3
3
|
* @fileoverview This audit identifies the time the page is "consistently interactive".
|
|
4
|
-
* Looks for the first period of at least 5 seconds after
|
|
4
|
+
* Looks for the first period of at least 5 seconds after FCP where both CPU and network were quiet,
|
|
5
5
|
* and returns the timestamp of the beginning of the CPU quiet period.
|
|
6
6
|
* @see https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/edit#
|
|
7
7
|
*/
|
|
@@ -18,7 +18,7 @@ const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* @fileoverview This audit identifies the time the page is "consistently interactive".
|
|
21
|
-
* Looks for the first period of at least 5 seconds after
|
|
21
|
+
* Looks for the first period of at least 5 seconds after FCP where both CPU and network were quiet,
|
|
22
22
|
* and returns the timestamp of the beginning of the CPU quiet period.
|
|
23
23
|
* @see https://docs.google.com/document/d/1GGiI9-7KeY3TPqS3YT271upUVimo-XiL5mwWorDUD4c/edit#
|
|
24
24
|
*/
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import {Audit} from './audit.js';
|
|
8
8
|
import * as i18n from '../lib/i18n/i18n.js';
|
|
9
9
|
import {LanternFirstContentfulPaint} from '../computed/metrics/lantern-first-contentful-paint.js';
|
|
10
|
-
import {LanternFirstMeaningfulPaint} from '../computed/metrics/lantern-first-meaningful-paint.js';
|
|
11
10
|
import {LanternInteractive} from '../computed/metrics/lantern-interactive.js';
|
|
12
11
|
import {LanternSpeedIndex} from '../computed/metrics/lantern-speed-index.js';
|
|
13
12
|
import {LanternLargestContentfulPaint} from '../computed/metrics/lantern-largest-contentful-paint.js';
|
|
@@ -52,7 +51,6 @@ class PredictivePerf extends Audit {
|
|
|
52
51
|
const settings = JSON.parse(JSON.stringify(defaultSettings)); // Use default settings.
|
|
53
52
|
const computationData = {trace, devtoolsLog, gatherContext, settings, URL};
|
|
54
53
|
const fcp = await LanternFirstContentfulPaint.request(computationData, context);
|
|
55
|
-
const fmp = await LanternFirstMeaningfulPaint.request(computationData, context);
|
|
56
54
|
const tti = await LanternInteractive.request(computationData, context);
|
|
57
55
|
const si = await LanternSpeedIndex.request(computationData, context);
|
|
58
56
|
const lcp = await LanternLargestContentfulPaint.request(computationData, context);
|
|
@@ -64,10 +62,6 @@ class PredictivePerf extends Audit {
|
|
|
64
62
|
optimisticFCP: fcp.optimisticEstimate.timeInMs,
|
|
65
63
|
pessimisticFCP: fcp.pessimisticEstimate.timeInMs,
|
|
66
64
|
|
|
67
|
-
roughEstimateOfFMP: fmp.timing,
|
|
68
|
-
optimisticFMP: fmp.optimisticEstimate.timeInMs,
|
|
69
|
-
pessimisticFMP: fmp.pessimisticEstimate.timeInMs,
|
|
70
|
-
|
|
71
65
|
roughEstimateOfTTI: tti.timing,
|
|
72
66
|
optimisticTTI: tti.optimisticEstimate.timeInMs,
|
|
73
67
|
pessimisticTTI: tti.pessimisticEstimate.timeInMs,
|
package/core/audits/redirects.js
CHANGED
|
@@ -90,6 +90,7 @@ class Redirects extends Audit {
|
|
|
90
90
|
|
|
91
91
|
const processedTrace = await ProcessedTrace.request(trace, context);
|
|
92
92
|
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
|
93
|
+
const documentRequests = Redirects.getDocumentRequestChain(networkRecords, processedTrace);
|
|
93
94
|
|
|
94
95
|
const metricComputationData = {trace, devtoolsLog, gatherContext, settings, URL: artifacts.URL};
|
|
95
96
|
const metricResult = await LanternInteractive.request(metricComputationData, context);
|
|
@@ -102,8 +103,6 @@ class Redirects extends Audit {
|
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
const documentRequests = Redirects.getDocumentRequestChain(networkRecords, processedTrace);
|
|
106
|
-
|
|
107
106
|
let totalWastedMs = 0;
|
|
108
107
|
const tableRows = [];
|
|
109
108
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import {makeComputedArtifact} from '../computed-artifact.js';
|
|
8
|
-
import {
|
|
8
|
+
import {LanternLargestContentfulPaint} from './lantern-largest-contentful-paint.js';
|
|
9
9
|
import {Interactive} from '../../lib/lantern/metrics/interactive.js';
|
|
10
10
|
import {getComputationDataParams, lanternErrorAdapter} from './lantern-metric.js';
|
|
11
11
|
|
|
@@ -29,8 +29,8 @@ class LanternInteractive extends Interactive {
|
|
|
29
29
|
* @return {Promise<LH.Artifacts.LanternMetric>}
|
|
30
30
|
*/
|
|
31
31
|
static async compute_(data, context) {
|
|
32
|
-
const
|
|
33
|
-
return this.computeMetricWithGraphs(data, context, {
|
|
32
|
+
const lcpResult = await LanternLargestContentfulPaint.request(data, context);
|
|
33
|
+
return this.computeMetricWithGraphs(data, context, {lcpResult});
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
export function getComputationDataParamsFromTrace(data: LH.Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext): Promise<{
|
|
6
6
|
simulator: import("../../lib/lantern/simulator/simulator.js").Simulator<any>;
|
|
7
7
|
graph: import("../page-dependency-graph.js").Node;
|
|
8
|
-
processedNavigation: import("../../
|
|
8
|
+
processedNavigation: import("../../lib/lantern/types/lantern.js").Simulation.ProcessedNavigation;
|
|
9
9
|
}>;
|
|
10
10
|
/**
|
|
11
11
|
* @param {LH.Artifacts.MetricComputationDataInput} data
|
|
@@ -23,7 +23,7 @@ export function getComputationDataParamsFromDevtoolsLog(data: LH.Artifacts.Metri
|
|
|
23
23
|
export function getComputationDataParams(data: LH.Artifacts.MetricComputationDataInput, context: LH.Artifacts.ComputedContext): Promise<{
|
|
24
24
|
simulator: import("../../lib/lantern/simulator/simulator.js").Simulator<any>;
|
|
25
25
|
graph: import("../page-dependency-graph.js").Node;
|
|
26
|
-
processedNavigation: import("../../
|
|
26
|
+
processedNavigation: import("../../lib/lantern/types/lantern.js").Simulation.ProcessedNavigation;
|
|
27
27
|
}>;
|
|
28
28
|
/**
|
|
29
29
|
* @param {unknown} err
|
|
@@ -9,6 +9,8 @@ import {LighthouseError} from '../../lib/lh-error.js';
|
|
|
9
9
|
import {LoadSimulator} from '../load-simulator.js';
|
|
10
10
|
import {ProcessedNavigation} from '../processed-navigation.js';
|
|
11
11
|
import {PageDependencyGraph} from '../page-dependency-graph.js';
|
|
12
|
+
import {TraceEngineResult} from '../trace-engine-result.js';
|
|
13
|
+
import {createProcessedNavigation} from '../../lib/lantern/lantern.js';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* @param {LH.Artifacts.MetricComputationDataInput} data
|
|
@@ -36,7 +38,8 @@ async function getComputationDataParamsFromTrace(data, context) {
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
const graph = await PageDependencyGraph.request({...data, fromTrace: true}, context);
|
|
39
|
-
const
|
|
41
|
+
const traceEngineResult = await TraceEngineResult.request(data, context);
|
|
42
|
+
const processedNavigation = createProcessedNavigation(traceEngineResult);
|
|
40
43
|
const simulator = data.simulator || (await LoadSimulator.request(data, context));
|
|
41
44
|
|
|
42
45
|
return {simulator, graph, processedNavigation};
|
|
@@ -9,7 +9,6 @@ import {ProcessedNavigation} from '../processed-navigation.js';
|
|
|
9
9
|
import {Speedline} from '../speedline.js';
|
|
10
10
|
import {FirstContentfulPaint} from './first-contentful-paint.js';
|
|
11
11
|
import {FirstContentfulPaintAllFrames} from './first-contentful-paint-all-frames.js';
|
|
12
|
-
import {FirstMeaningfulPaint} from './first-meaningful-paint.js';
|
|
13
12
|
import {LargestContentfulPaint} from './largest-contentful-paint.js';
|
|
14
13
|
import {LargestContentfulPaintAllFrames} from './largest-contentful-paint-all-frames.js';
|
|
15
14
|
import {Interactive} from './interactive.js';
|
|
@@ -51,7 +50,6 @@ class TimingSummary {
|
|
|
51
50
|
const speedline = await Speedline.request(trace, context);
|
|
52
51
|
const firstContentfulPaint = await requestOrUndefined(FirstContentfulPaint, metricComputationData);
|
|
53
52
|
const firstContentfulPaintAllFrames = await requestOrUndefined(FirstContentfulPaintAllFrames, metricComputationData);
|
|
54
|
-
const firstMeaningfulPaint = await requestOrUndefined(FirstMeaningfulPaint, metricComputationData);
|
|
55
53
|
const largestContentfulPaint = await requestOrUndefined(LargestContentfulPaint, metricComputationData);
|
|
56
54
|
const largestContentfulPaintAllFrames = await requestOrUndefined(LargestContentfulPaintAllFrames, metricComputationData);
|
|
57
55
|
const interactive = await requestOrUndefined(Interactive, metricComputationData);
|
|
@@ -74,8 +72,6 @@ class TimingSummary {
|
|
|
74
72
|
firstContentfulPaintTs: firstContentfulPaint?.timestamp,
|
|
75
73
|
firstContentfulPaintAllFrames: firstContentfulPaintAllFrames?.timing,
|
|
76
74
|
firstContentfulPaintAllFramesTs: firstContentfulPaintAllFrames?.timestamp,
|
|
77
|
-
firstMeaningfulPaint: firstMeaningfulPaint?.timing,
|
|
78
|
-
firstMeaningfulPaintTs: firstMeaningfulPaint?.timestamp,
|
|
79
75
|
largestContentfulPaint: largestContentfulPaint?.timing,
|
|
80
76
|
largestContentfulPaintTs: largestContentfulPaint?.timestamp,
|
|
81
77
|
largestContentfulPaintAllFrames: largestContentfulPaintAllFrames?.timing,
|
|
@@ -107,8 +103,6 @@ class TimingSummary {
|
|
|
107
103
|
observedFirstContentfulPaintTs: processedNavigation?.timestamps.firstContentfulPaint,
|
|
108
104
|
observedFirstContentfulPaintAllFrames: processedNavigation?.timings.firstContentfulPaintAllFrames,
|
|
109
105
|
observedFirstContentfulPaintAllFramesTs: processedNavigation?.timestamps.firstContentfulPaintAllFrames,
|
|
110
|
-
observedFirstMeaningfulPaint: processedNavigation?.timings.firstMeaningfulPaint,
|
|
111
|
-
observedFirstMeaningfulPaintTs: processedNavigation?.timestamps.firstMeaningfulPaint,
|
|
112
106
|
observedLargestContentfulPaint: processedNavigation?.timings.largestContentfulPaint,
|
|
113
107
|
observedLargestContentfulPaintTs: processedNavigation?.timestamps.largestContentfulPaint,
|
|
114
108
|
observedLargestContentfulPaintAllFrames: processedNavigation?.timings.largestContentfulPaintAllFrames,
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
1
|
+
export type MetricName = import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricName;
|
|
2
|
+
export type MetricScore = import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricScore;
|
|
3
|
+
/** @typedef {import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricName} MetricName */
|
|
4
|
+
/** @typedef {import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricScore} MetricScore */
|
|
6
5
|
/** @type {LH.Util.SelfMap<LH.Crdp.Network.ResourceType>} */
|
|
7
6
|
export const NetworkRequestTypes: LH.Util.SelfMap<LH.Crdp.Network.ResourceType>;
|
|
7
|
+
/**
|
|
8
|
+
* @param {LH.Artifacts.TraceEngineResult} traceEngineResult
|
|
9
|
+
* @return {Lantern.Simulation.ProcessedNavigation}
|
|
10
|
+
*/
|
|
11
|
+
export function createProcessedNavigation(traceEngineResult: LH.Artifacts.TraceEngineResult): Lantern.Simulation.ProcessedNavigation;
|
|
12
|
+
import * as Lantern from './types/lantern.js';
|
|
8
13
|
//# sourceMappingURL=lantern.d.ts.map
|
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import * as Lantern from './types/lantern.js';
|
|
8
|
+
|
|
9
|
+
/** @typedef {import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricName} MetricName */
|
|
10
|
+
/** @typedef {import('@paulirish/trace_engine/models/trace/handlers/PageLoadMetricsHandler.js').MetricScore} MetricScore */
|
|
11
|
+
|
|
7
12
|
/** @type {LH.Util.SelfMap<LH.Crdp.Network.ResourceType>} */
|
|
8
13
|
const NetworkRequestTypes = {
|
|
9
14
|
XHR: 'XHR',
|
|
@@ -26,6 +31,48 @@ const NetworkRequestTypes = {
|
|
|
26
31
|
Prefetch: 'Prefetch',
|
|
27
32
|
};
|
|
28
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @param {LH.Artifacts.TraceEngineResult} traceEngineResult
|
|
36
|
+
* @return {Lantern.Simulation.ProcessedNavigation}
|
|
37
|
+
*/
|
|
38
|
+
function createProcessedNavigation(traceEngineResult) {
|
|
39
|
+
const Meta = traceEngineResult.data.Meta;
|
|
40
|
+
const frameId = Meta.mainFrameId;
|
|
41
|
+
const scoresByNav = traceEngineResult.data.PageLoadMetrics.metricScoresByFrameId.get(frameId);
|
|
42
|
+
if (!scoresByNav) {
|
|
43
|
+
throw new Error('missing metric scores for main frame');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lastNavigationId = Meta.mainFrameNavigations.at(-1)?.args.data?.navigationId;
|
|
47
|
+
const scores = lastNavigationId && scoresByNav.get(lastNavigationId);
|
|
48
|
+
if (!scores) {
|
|
49
|
+
throw new Error('missing metric scores for specified navigation');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @param {MetricName} metric */
|
|
53
|
+
const getTimestampOrUndefined = metric => {
|
|
54
|
+
const metricScore = scores.get(metric);
|
|
55
|
+
if (!metricScore?.event) return;
|
|
56
|
+
return metricScore.event.ts;
|
|
57
|
+
};
|
|
58
|
+
/** @param {MetricName} metric */
|
|
59
|
+
const getTimestamp = metric => {
|
|
60
|
+
const metricScore = scores.get(metric);
|
|
61
|
+
if (!metricScore?.event) throw new Error(`missing metric: ${metric}`);
|
|
62
|
+
return metricScore.event.ts;
|
|
63
|
+
};
|
|
64
|
+
// TODO: should use `MetricName.LCP`, but it is a const enum.
|
|
65
|
+
const FCP = /** @type {MetricName} */('FCP');
|
|
66
|
+
const LCP = /** @type {MetricName} */('LCP');
|
|
67
|
+
return {
|
|
68
|
+
timestamps: {
|
|
69
|
+
firstContentfulPaint: getTimestamp(FCP),
|
|
70
|
+
largestContentfulPaint: getTimestampOrUndefined(LCP),
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
29
75
|
export {
|
|
30
76
|
NetworkRequestTypes,
|
|
77
|
+
createProcessedNavigation,
|
|
31
78
|
};
|
|
@@ -4,7 +4,7 @@ export type Simulator = import('./simulator/simulator.js').Simulator;
|
|
|
4
4
|
export type Extras = {
|
|
5
5
|
optimistic: boolean;
|
|
6
6
|
fcpResult?: LH.Artifacts.LanternMetric | undefined;
|
|
7
|
-
|
|
7
|
+
lcpResult?: LH.Artifacts.LanternMetric | undefined;
|
|
8
8
|
interactiveResult?: LH.Artifacts.LanternMetric | undefined;
|
|
9
9
|
speedline?: {
|
|
10
10
|
speedIndex: number;
|
|
@@ -17,7 +17,7 @@ export type Extras = {
|
|
|
17
17
|
* @typedef Extras
|
|
18
18
|
* @property {boolean} optimistic
|
|
19
19
|
* @property {LH.Artifacts.LanternMetric=} fcpResult
|
|
20
|
-
* @property {LH.Artifacts.LanternMetric=}
|
|
20
|
+
* @property {LH.Artifacts.LanternMetric=} lcpResult
|
|
21
21
|
* @property {LH.Artifacts.LanternMetric=} interactiveResult
|
|
22
22
|
* @property {{speedIndex: number}=} speedline
|
|
23
23
|
*/
|
|
@@ -44,16 +44,16 @@ export class Metric {
|
|
|
44
44
|
static getScaledCoefficients(rttMs: number): Lantern.Simulation.MetricCoefficients;
|
|
45
45
|
/**
|
|
46
46
|
* @param {Node} dependencyGraph
|
|
47
|
-
* @param {
|
|
47
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
48
48
|
* @return {Node}
|
|
49
49
|
*/
|
|
50
|
-
static getOptimisticGraph(dependencyGraph: Node, processedNavigation:
|
|
50
|
+
static getOptimisticGraph(dependencyGraph: Node, processedNavigation: Lantern.Simulation.ProcessedNavigation): Node;
|
|
51
51
|
/**
|
|
52
52
|
* @param {Node} dependencyGraph
|
|
53
|
-
* @param {
|
|
53
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
54
54
|
* @return {Node}
|
|
55
55
|
*/
|
|
56
|
-
static getPessimisticGraph(dependencyGraph: Node, processedNavigation:
|
|
56
|
+
static getPessimisticGraph(dependencyGraph: Node, processedNavigation: Lantern.Simulation.ProcessedNavigation): Node;
|
|
57
57
|
/**
|
|
58
58
|
* @param {Lantern.Simulation.Result} simulationResult
|
|
59
59
|
* @param {Extras} extras
|
|
@@ -16,7 +16,7 @@ import {RESOURCE_TYPES} from '../../lib/network-request.js';
|
|
|
16
16
|
* @typedef Extras
|
|
17
17
|
* @property {boolean} optimistic
|
|
18
18
|
* @property {LH.Artifacts.LanternMetric=} fcpResult
|
|
19
|
-
* @property {LH.Artifacts.LanternMetric=}
|
|
19
|
+
* @property {LH.Artifacts.LanternMetric=} lcpResult
|
|
20
20
|
* @property {LH.Artifacts.LanternMetric=} interactiveResult
|
|
21
21
|
* @property {{speedIndex: number}=} speedline
|
|
22
22
|
*/
|
|
@@ -64,7 +64,7 @@ class Metric {
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* @param {Node} dependencyGraph
|
|
67
|
-
* @param {
|
|
67
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
68
68
|
* @return {Node}
|
|
69
69
|
*/
|
|
70
70
|
static getOptimisticGraph(dependencyGraph, processedNavigation) { // eslint-disable-line no-unused-vars
|
|
@@ -73,7 +73,7 @@ class Metric {
|
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* @param {Node} dependencyGraph
|
|
76
|
-
* @param {
|
|
76
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
77
77
|
* @return {Node}
|
|
78
78
|
*/
|
|
79
79
|
static getPessimisticGraph(dependencyGraph, processedNavigation) { // eslint-disable-line no-unused-vars
|
|
@@ -170,7 +170,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
170
170
|
|
|
171
171
|
/**
|
|
172
172
|
* @param {Node} dependencyGraph
|
|
173
|
-
* @param {
|
|
173
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
174
174
|
* @return {Node}
|
|
175
175
|
*/
|
|
176
176
|
static getOptimisticGraph(dependencyGraph, processedNavigation) {
|
|
@@ -186,7 +186,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
186
186
|
|
|
187
187
|
/**
|
|
188
188
|
* @param {Node} dependencyGraph
|
|
189
|
-
* @param {
|
|
189
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
190
190
|
* @return {Node}
|
|
191
191
|
*/
|
|
192
192
|
static getPessimisticGraph(dependencyGraph, processedNavigation) {
|
|
@@ -66,12 +66,12 @@ class Interactive extends Metric {
|
|
|
66
66
|
* @return {LH.Gatherer.Simulation.Result}
|
|
67
67
|
*/
|
|
68
68
|
static getEstimateFromSimulation(simulationResult, extras) {
|
|
69
|
-
if (!extras.
|
|
69
|
+
if (!extras.lcpResult) throw new Error('missing lcpResult');
|
|
70
70
|
|
|
71
71
|
const lastTaskAt = Interactive.getLastLongTaskEndTime(simulationResult.nodeTimings);
|
|
72
72
|
const minimumTime = extras.optimistic
|
|
73
|
-
? extras.
|
|
74
|
-
: extras.
|
|
73
|
+
? extras.lcpResult.optimisticEstimate.timeInMs
|
|
74
|
+
: extras.lcpResult.pessimisticEstimate.timeInMs;
|
|
75
75
|
return {
|
|
76
76
|
timeInMs: Math.max(minimumTime, lastTaskAt),
|
|
77
77
|
nodeTimings: simulationResult.nodeTimings,
|
|
@@ -84,13 +84,13 @@ class Interactive extends Metric {
|
|
|
84
84
|
* @return {Promise<LH.Artifacts.LanternMetric>}
|
|
85
85
|
*/
|
|
86
86
|
static async compute(data, extras) {
|
|
87
|
-
const
|
|
88
|
-
if (!
|
|
89
|
-
throw new Error('
|
|
87
|
+
const lcpResult = extras?.lcpResult;
|
|
88
|
+
if (!lcpResult) {
|
|
89
|
+
throw new Error('LCP is required to calculate the Interactive metric');
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
const metricResult = await super.compute(data, extras);
|
|
93
|
-
metricResult.timing = Math.max(metricResult.timing,
|
|
93
|
+
metricResult.timing = Math.max(metricResult.timing, lcpResult.timing);
|
|
94
94
|
return metricResult;
|
|
95
95
|
}
|
|
96
96
|
|
|
@@ -39,7 +39,7 @@ class LargestContentfulPaint extends Metric {
|
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* @param {Node} dependencyGraph
|
|
42
|
-
* @param {
|
|
42
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
43
43
|
* @return {Node}
|
|
44
44
|
*/
|
|
45
45
|
static getOptimisticGraph(dependencyGraph, processedNavigation) {
|
|
@@ -56,7 +56,7 @@ class LargestContentfulPaint extends Metric {
|
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
58
|
* @param {Node} dependencyGraph
|
|
59
|
-
* @param {
|
|
59
|
+
* @param {Lantern.Simulation.ProcessedNavigation} processedNavigation
|
|
60
60
|
* @return {Node}
|
|
61
61
|
*/
|
|
62
62
|
static getPessimisticGraph(dependencyGraph, processedNavigation) {
|
|
@@ -169,9 +169,16 @@ export namespace Simulation {
|
|
|
169
169
|
nodeTimings: Map<GraphNode<T>, NodeTiming>;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
interface ProcessedNavigation {
|
|
173
|
+
timestamps: {
|
|
174
|
+
firstContentfulPaint: number;
|
|
175
|
+
largestContentfulPaint?: number;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
172
179
|
interface MetricComputationDataInput {
|
|
173
180
|
simulator: Simulator<any>;
|
|
174
181
|
graph: GraphNode<any>;
|
|
175
|
-
processedNavigation:
|
|
182
|
+
processedNavigation: ProcessedNavigation;
|
|
176
183
|
}
|
|
177
184
|
}
|
|
@@ -281,11 +281,9 @@ export class TraceProcessor {
|
|
|
281
281
|
timeOriginEvt: import("../..").TraceEvent;
|
|
282
282
|
firstPaintEvt: import("../..").TraceEvent | undefined;
|
|
283
283
|
firstContentfulPaintEvt: import("../..").TraceEvent;
|
|
284
|
-
firstMeaningfulPaintEvt: import("../..").TraceEvent | undefined;
|
|
285
284
|
largestContentfulPaintEvt: LCPEvent | undefined;
|
|
286
285
|
loadEvt: import("../..").TraceEvent | undefined;
|
|
287
286
|
domContentLoadedEvt: import("../..").TraceEvent | undefined;
|
|
288
|
-
fmpFellBack: boolean;
|
|
289
287
|
lcpInvalidated: boolean;
|
|
290
288
|
};
|
|
291
289
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* various tools. This artifact will take a trace and then:
|
|
10
10
|
*
|
|
11
11
|
* 1. Find the TracingStartedInPage and navigationStart events of our intended tab & frame.
|
|
12
|
-
* 2. Find the firstContentfulPaint and marked
|
|
12
|
+
* 2. Find the firstContentfulPaint and marked largestContentfulPaint events
|
|
13
13
|
* 3. Isolate only the trace events from the tab's process (including all threads like compositor)
|
|
14
14
|
* * Sort those trace events in chronological order (as order isn't guaranteed)
|
|
15
15
|
* 4. Return all those items in one handy bundle.
|
|
@@ -835,7 +835,6 @@ class TraceProcessor {
|
|
|
835
835
|
firstPaint: frameTimings.timings.firstPaint,
|
|
836
836
|
firstContentfulPaint: frameTimings.timings.firstContentfulPaint,
|
|
837
837
|
firstContentfulPaintAllFrames: getTiming(fcpAllFramesEvt.ts),
|
|
838
|
-
firstMeaningfulPaint: frameTimings.timings.firstMeaningfulPaint,
|
|
839
838
|
largestContentfulPaint: frameTimings.timings.largestContentfulPaint,
|
|
840
839
|
largestContentfulPaintAllFrames: maybeGetTiming(lcpAllFramesEvt?.ts),
|
|
841
840
|
load: frameTimings.timings.load,
|
|
@@ -847,7 +846,6 @@ class TraceProcessor {
|
|
|
847
846
|
firstPaint: frameTimings.timestamps.firstPaint,
|
|
848
847
|
firstContentfulPaint: frameTimings.timestamps.firstContentfulPaint,
|
|
849
848
|
firstContentfulPaintAllFrames: fcpAllFramesEvt.ts,
|
|
850
|
-
firstMeaningfulPaint: frameTimings.timestamps.firstMeaningfulPaint,
|
|
851
849
|
largestContentfulPaint: frameTimings.timestamps.largestContentfulPaint,
|
|
852
850
|
largestContentfulPaintAllFrames: lcpAllFramesEvt?.ts,
|
|
853
851
|
load: frameTimings.timestamps.load,
|
|
@@ -857,12 +855,10 @@ class TraceProcessor {
|
|
|
857
855
|
firstPaintEvt: frameTimings.firstPaintEvt,
|
|
858
856
|
firstContentfulPaintEvt: frameTimings.firstContentfulPaintEvt,
|
|
859
857
|
firstContentfulPaintAllFramesEvt: fcpAllFramesEvt,
|
|
860
|
-
firstMeaningfulPaintEvt: frameTimings.firstMeaningfulPaintEvt,
|
|
861
858
|
largestContentfulPaintEvt: frameTimings.largestContentfulPaintEvt,
|
|
862
859
|
largestContentfulPaintAllFramesEvt: lcpAllFramesEvt,
|
|
863
860
|
loadEvt: frameTimings.loadEvt,
|
|
864
861
|
domContentLoadedEvt: frameTimings.domContentLoadedEvt,
|
|
865
|
-
fmpFellBack: frameTimings.fmpFellBack,
|
|
866
862
|
lcpInvalidated: frameTimings.lcpInvalidated,
|
|
867
863
|
};
|
|
868
864
|
}
|
|
@@ -970,28 +966,6 @@ class TraceProcessor {
|
|
|
970
966
|
throw this.createNoFirstContentfulPaintError();
|
|
971
967
|
}
|
|
972
968
|
|
|
973
|
-
// fMP will follow at/after the FP
|
|
974
|
-
let firstMeaningfulPaint = frameEvents.find(
|
|
975
|
-
e => e.name === 'firstMeaningfulPaint' && e.ts > timeOriginEvt.ts
|
|
976
|
-
);
|
|
977
|
-
let fmpFellBack = false;
|
|
978
|
-
|
|
979
|
-
// If there was no firstMeaningfulPaint event found in the trace, the network idle detection
|
|
980
|
-
// may have not been triggered before Lighthouse finished tracing.
|
|
981
|
-
// In this case, we'll use the last firstMeaningfulPaintCandidate we can find.
|
|
982
|
-
// However, if no candidates were found (a bogus trace, likely), we fail.
|
|
983
|
-
if (!firstMeaningfulPaint) {
|
|
984
|
-
const fmpCand = 'firstMeaningfulPaintCandidate';
|
|
985
|
-
fmpFellBack = true;
|
|
986
|
-
log.verbose('TraceProcessor',
|
|
987
|
-
`No firstMeaningfulPaint found, falling back to last ${fmpCand}`);
|
|
988
|
-
const lastCandidate = frameEvents.filter(e => e.name === fmpCand).pop();
|
|
989
|
-
if (!lastCandidate) {
|
|
990
|
-
log.verbose('TraceProcessor', 'No `firstMeaningfulPaintCandidate` events found in trace');
|
|
991
|
-
}
|
|
992
|
-
firstMeaningfulPaint = lastCandidate;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
969
|
// This function accepts events spanning multiple frames, but this usage will only provide events from the main frame.
|
|
996
970
|
const lcpResult = this.computeValidLCPAllFrames(frameEvents, timeOriginEvt);
|
|
997
971
|
|
|
@@ -1007,7 +981,6 @@ class TraceProcessor {
|
|
|
1007
981
|
timeOrigin: timeOriginEvt.ts,
|
|
1008
982
|
firstPaint: getTimestamp(firstPaint),
|
|
1009
983
|
firstContentfulPaint: firstContentfulPaint.ts,
|
|
1010
|
-
firstMeaningfulPaint: getTimestamp(firstMeaningfulPaint),
|
|
1011
984
|
largestContentfulPaint: getTimestamp(lcpResult.lcp),
|
|
1012
985
|
load: getTimestamp(load),
|
|
1013
986
|
domContentLoaded: getTimestamp(domContentLoaded),
|
|
@@ -1022,7 +995,6 @@ class TraceProcessor {
|
|
|
1022
995
|
timeOrigin: 0,
|
|
1023
996
|
firstPaint: maybeGetTiming(timestamps.firstPaint),
|
|
1024
997
|
firstContentfulPaint: getTiming(timestamps.firstContentfulPaint),
|
|
1025
|
-
firstMeaningfulPaint: maybeGetTiming(timestamps.firstMeaningfulPaint),
|
|
1026
998
|
largestContentfulPaint: maybeGetTiming(timestamps.largestContentfulPaint),
|
|
1027
999
|
load: maybeGetTiming(timestamps.load),
|
|
1028
1000
|
domContentLoaded: maybeGetTiming(timestamps.domContentLoaded),
|
|
@@ -1034,11 +1006,9 @@ class TraceProcessor {
|
|
|
1034
1006
|
timeOriginEvt: timeOriginEvt,
|
|
1035
1007
|
firstPaintEvt: firstPaint,
|
|
1036
1008
|
firstContentfulPaintEvt: firstContentfulPaint,
|
|
1037
|
-
firstMeaningfulPaintEvt: firstMeaningfulPaint,
|
|
1038
1009
|
largestContentfulPaintEvt: lcpResult.lcp,
|
|
1039
1010
|
loadEvt: load,
|
|
1040
1011
|
domContentLoadedEvt: domContentLoaded,
|
|
1041
|
-
fmpFellBack,
|
|
1042
1012
|
lcpInvalidated: lcpResult.invalidated,
|
|
1043
1013
|
};
|
|
1044
1014
|
}
|