@@ -7,6 +7,8 @@ import { observe } from './web-vitals/lib/observe';
7
7
8
8
type InstrumentHandlerTypePerformanceObserver = 'longtask' | 'event' | 'navigation' | 'paint' | 'resource' ;
9
9
10
+ type InstrumentHandlerTypeMetric = 'cls' | 'lcp' | 'fid' ;
11
+
10
12
// We provide this here manually instead of relying on a global, as this is not available in non-browser environements
11
13
// And we do not want to expose such types
12
14
interface PerformanceEntry {
@@ -68,45 +70,42 @@ interface Metric {
68
70
navigationType : 'navigate' | 'reload' | 'back-forward' | 'back-forward-cache' | 'prerender' ;
69
71
}
70
72
71
- export type InstrumentHandlerType = 'cls' | 'lcp' | 'fid' | InstrumentHandlerTypePerformanceObserver ;
73
+ type InstrumentHandlerType = InstrumentHandlerTypeMetric | InstrumentHandlerTypePerformanceObserver ;
72
74
73
75
// eslint-disable-next-line @typescript-eslint/no-explicit-any
74
- export type InstrumentHandlerCallback = ( data : any ) => void ;
76
+ type InstrumentHandlerCallback = ( data : any ) => void ;
75
77
76
78
type CleanupHandlerCallback = ( ) => void ;
77
79
78
80
const handlers : { [ key in InstrumentHandlerType ] ?: InstrumentHandlerCallback [ ] } = { } ;
79
81
const instrumented : { [ key in InstrumentHandlerType ] ?: boolean } = { } ;
80
82
81
- /** Instruments given API */
82
- function instrument ( type : InstrumentHandlerType ) : void {
83
- if ( instrumented [ type ] ) {
84
- return ;
85
- }
83
+ let _previousCls : Metric | undefined ;
84
+ let _previousFid : Metric | undefined ;
85
+ let _previousLcp : Metric | undefined ;
86
86
87
- instrumented [ type ] = true ;
88
-
89
- switch ( type ) {
90
- case 'cls' :
91
- instrumentCls ( ) ;
92
- break ;
93
- case 'fid' :
94
- instrumentFid ( ) ;
95
- break ;
96
- case 'lcp' :
97
- instrumentLcp ( ) ;
98
- break ;
99
- case 'longtask' :
100
- case 'event' :
101
- case 'navigation' :
102
- case 'paint' :
103
- case 'resource' :
104
- instrumentPerformanceObserver ( type ) ;
105
- break ;
106
- default :
107
- __DEBUG_BUILD__ && logger . warn ( 'unknown instrumentation type:' , type ) ;
108
- return ;
109
- }
87
+ /**
88
+ * Add a callback that will be triggered when a CLS metric is available.
89
+ * Returns a cleanup callback which can be called to remove the instrumentation handler.
90
+ */
91
+ export function addClsInstrumentationHandler ( callback : ( data : { metric : Metric } ) => void ) : CleanupHandlerCallback {
92
+ return addMetricObserver ( 'cls' , callback , instrumentCls , _previousCls ) ;
93
+ }
94
+
95
+ /**
96
+ * Add a callback that will be triggered when a LCP metric is available.
97
+ * Returns a cleanup callback which can be called to remove the instrumentation handler.
98
+ */
99
+ export function addLcpInstrumentationHandler ( callback : ( data : { metric : Metric } ) => void ) : CleanupHandlerCallback {
100
+ return addMetricObserver ( 'lcp' , callback , instrumentLcp , _previousLcp ) ;
101
+ }
102
+
103
+ /**
104
+ * Add a callback that will be triggered when a FID metric is available.
105
+ * Returns a cleanup callback which can be called to remove the instrumentation handler.
106
+ */
107
+ export function addFidInstrumentationHandler ( callback : ( data : { metric : Metric } ) => void ) : CleanupHandlerCallback {
108
+ return addMetricObserver ( 'fid' , callback , instrumentFid , _previousFid ) ;
110
109
}
111
110
112
111
export function addPerformanceInstrumentationHandler (
@@ -117,48 +116,24 @@ export function addPerformanceInstrumentationHandler(
117
116
type : InstrumentHandlerTypePerformanceObserver ,
118
117
callback : ( data : { entries : PerformanceEntry [ ] } ) => void ,
119
118
) : CleanupHandlerCallback ;
120
- export function addPerformanceInstrumentationHandler (
121
- type : 'lcp' | 'cls' | 'fid' ,
122
- callback : ( data : { metric : Metric } ) => void ,
123
- ) : CleanupHandlerCallback ;
124
119
125
120
/**
126
- * Add handler that will be called when given type of instrumentation triggers.
127
- * Use at your own risk, this might break without changelog notice, only used internally .
128
- * @hidden
121
+ * Add a callback that will be triggered when a performance observer is triggered,
122
+ * and receives the entries of the observer .
123
+ * Returns a cleanup callback which can be called to remove the instrumentation handler.
129
124
*/
130
125
export function addPerformanceInstrumentationHandler (
131
- type : InstrumentHandlerType ,
132
- callback : InstrumentHandlerCallback ,
126
+ type : InstrumentHandlerTypePerformanceObserver ,
127
+ callback : ( data : { entries : PerformanceEntry [ ] } ) => void ,
133
128
) : CleanupHandlerCallback {
134
- handlers [ type ] = handlers [ type ] || [ ] ;
135
- ( handlers [ type ] as InstrumentHandlerCallback [ ] ) . push ( callback ) ;
136
- instrument ( type ) ;
129
+ addHandler ( type , callback ) ;
137
130
138
- // Metrics may have been sent before, in which case we still want to trigger callbacks
139
- if ( type === 'cls' && _previousCls ) {
140
- callback ( { metric : _previousCls } ) ;
141
- }
142
- if ( type === 'fid' && _previousFid ) {
143
- callback ( { metric : _previousFid } ) ;
144
- }
145
- if ( type === 'lcp' && _previousLcp ) {
146
- callback ( { metric : _previousLcp } ) ;
131
+ if ( ! instrumented [ type ] ) {
132
+ instrumentPerformanceObserver ( type ) ;
133
+ instrumented [ type ] = true ;
147
134
}
148
135
149
- // Return a function to remove the handler
150
- return ( ) => {
151
- const typeHandlers = handlers [ type ] ;
152
-
153
- if ( ! typeHandlers ) {
154
- return ;
155
- }
156
-
157
- const index = typeHandlers . indexOf ( callback ) ;
158
- if ( index !== - 1 ) {
159
- typeHandlers . splice ( index , 1 ) ;
160
- }
161
- } ;
136
+ return getCleanupCallback ( type , callback ) ;
162
137
}
163
138
164
139
/** Trigger all handlers of a given type. */
@@ -182,13 +157,11 @@ function triggerHandlers(type: InstrumentHandlerType, data: unknown): void {
182
157
}
183
158
}
184
159
185
- let _previousCls : Metric | undefined ;
186
- let _previousFid : Metric | undefined ;
187
- let _previousLcp : Metric | undefined ;
188
-
189
160
function instrumentCls ( ) : void {
190
161
onCLS ( metric => {
191
- triggerHandlers ( 'cls' , { metric } ) ;
162
+ triggerHandlers ( 'cls' , {
163
+ metric,
164
+ } ) ;
192
165
_previousCls = metric ;
193
166
} ) ;
194
167
}
@@ -211,6 +184,26 @@ function instrumentLcp(): void {
211
184
} ) ;
212
185
}
213
186
187
+ function addMetricObserver (
188
+ type : InstrumentHandlerTypeMetric ,
189
+ callback : InstrumentHandlerCallback ,
190
+ instrumentFn : ( ) => void ,
191
+ previousValue : Metric | undefined ,
192
+ ) : CleanupHandlerCallback {
193
+ addHandler ( type , callback ) ;
194
+
195
+ if ( ! instrumented [ type ] ) {
196
+ instrumentFn ( ) ;
197
+ instrumented [ type ] = true ;
198
+ }
199
+
200
+ if ( previousValue ) {
201
+ callback ( { metric : previousValue } ) ;
202
+ }
203
+
204
+ return getCleanupCallback ( type , callback ) ;
205
+ }
206
+
214
207
function instrumentPerformanceObserver ( type : InstrumentHandlerTypePerformanceObserver ) : void {
215
208
const options : PerformanceObserverInit = { } ;
216
209
@@ -227,3 +220,24 @@ function instrumentPerformanceObserver(type: InstrumentHandlerTypePerformanceObs
227
220
options ,
228
221
) ;
229
222
}
223
+
224
+ function addHandler ( type : InstrumentHandlerType , handler : InstrumentHandlerCallback ) : void {
225
+ handlers [ type ] = handlers [ type ] || [ ] ;
226
+ ( handlers [ type ] as InstrumentHandlerCallback [ ] ) . push ( handler ) ;
227
+ }
228
+
229
+ // Get a callback which can be called to remove the instrumentation handler
230
+ function getCleanupCallback ( type : InstrumentHandlerType , callback : InstrumentHandlerCallback ) : CleanupHandlerCallback {
231
+ return ( ) => {
232
+ const typeHandlers = handlers [ type ] ;
233
+
234
+ if ( ! typeHandlers ) {
235
+ return ;
236
+ }
237
+
238
+ const index = typeHandlers . indexOf ( callback ) ;
239
+ if ( index !== - 1 ) {
240
+ typeHandlers . splice ( index , 1 ) ;
241
+ }
242
+ } ;
243
+ }
0 commit comments