sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
76 lines (69 loc) • 2.31 kB
text/typescript
import {useTelemetry} from '@sanity/telemetry/react'
import {useCallback, useEffect} from 'react'
import {PerformanceINPMeasured} from '../__telemetry__/performance.telemetry'
function getInterestingAttrs(node: Node | null): undefined | {ui?: string; testId?: string} {
if (!node || !(node instanceof Element)) {
return undefined
}
const ui = node.getAttribute('data-ui') || undefined
const testId = node.getAttribute('data-testid') || undefined
return ui || testId ? {ui, testId} : undefined
}
function getElementIdentifier(node: Node | null) {
if (!node) {
return null
}
if (!(node instanceof Element)) {
return node.nodeName
}
// Note: Deliberately using classList instead of className here since className isn't always a string
// See https://developer.mozilla.org/en-US/docs/Web/API/Element/className#notes
const {nodeName, classList, id} = node
return (
nodeName.toLowerCase() +
(id ? `#${id}` : '') +
(classList ? `.${classList.value.replaceAll(' ', '.')}` : '')
)
}
function isPerformanceEventTiming(entry: PerformanceEntry): entry is PerformanceEventTiming {
return entry.entryType === 'event'
}
/**
* @internal
*/
export function useMeasurePerformanceTelemetry() {
const telemetry = useTelemetry()
const onEvent = useCallback(
(list: PerformanceObserverEntryList, observer: PerformanceObserver) => {
const entries = list.getEntries()
let maxEntry: PerformanceEventTiming | undefined = undefined
for (const entry of entries) {
if (!isPerformanceEventTiming(entry)) continue
if (entry.duration > (maxEntry?.duration || 0)) {
maxEntry = entry
}
}
if (!maxEntry) {
return
}
telemetry.log(PerformanceINPMeasured, {
target: getElementIdentifier(maxEntry.target),
attrs: getInterestingAttrs(maxEntry.target),
interaction: maxEntry.name,
duration: maxEntry.duration,
})
},
[telemetry],
)
useEffect(() => {
if (!('PerformanceObserver' in globalThis)) {
return
}
const observer = new PerformanceObserver(onEvent)
observer.observe({type: 'event', buffered: true})
// eslint-disable-next-line consistent-return
return () => {
observer.disconnect()
}
}, [onEvent])
}