-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use the script instead of web-vitals directly (#4)
- Loading branch information
1 parent
67d7c14
commit 1e058c9
Showing
13 changed files
with
183 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ yarn-debug.log* | |
yarn-error.log* | ||
.pnpm-debug.log* | ||
|
||
.env | ||
# local env files | ||
.env*.local | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
export default function Blog() { | ||
return <div>BLOG Testing speed insights</div>; | ||
return <div>My blog page</div>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
import Link from 'next/link'; | ||
|
||
export default function Blog() { | ||
return <div>BLOG Testing speed insights</div>; | ||
return ( | ||
<div> | ||
<h1>Welcome to the Blog</h1> | ||
<Link href="/blog/test">First blog entry</Link> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { name as packageName, version } from '../package.json'; | ||
import { initQueue } from './queue'; | ||
import type { SpeedInsightsProps } from './types'; | ||
import { isBrowser, isDevelopment } from './utils'; | ||
|
||
const SCRIPT_URL = `/v1/speed-insights`; | ||
const SCRIPT_PROD_NAME = 'script.js'; | ||
const SCRIPT_DEBUG_NAME = 'script.debug.js'; | ||
|
||
/** | ||
* Injects the Vercel Speed Insights script into the page head and starts tracking page views. Read more in our [documentation](https://vercel.com/docs/speed-insights). | ||
* @param [props] - Speed Insights options. | ||
* @param [props.debug] - Whether to enable debug logging in development. Defaults to `true`. | ||
* @param [props.beforeSend] - A middleware function to modify events before they are sent. Should return the event object or `null` to cancel the event. | ||
*/ | ||
function inject(props: SpeedInsightsProps): { | ||
setDynamicPath: (path: string) => void; | ||
} | null { | ||
if (!isBrowser()) return null; | ||
|
||
initQueue(); | ||
|
||
if (props.beforeSend) { | ||
window.si?.('beforeSend', props.beforeSend); | ||
} | ||
const src = | ||
props.scriptSrc || | ||
`${SCRIPT_URL}/${isDevelopment() ? SCRIPT_DEBUG_NAME : SCRIPT_PROD_NAME}`; | ||
|
||
if (document.head.querySelector(`script[src*="${src}"]`)) return null; | ||
|
||
const script = document.createElement('script'); | ||
script.src = src; | ||
script.defer = true; | ||
script.setAttribute('data-sdkn', packageName); | ||
script.setAttribute('data-sdkv', version); | ||
|
||
if (props.sampleRate) { | ||
script.setAttribute('data-sample-rate', props.sampleRate.toString()); | ||
} | ||
if (props.dynamicPath) { | ||
script.setAttribute('data-dynamic-path', props.dynamicPath); | ||
} | ||
if (props.endpoint) { | ||
script.setAttribute('data-endpoint', props.endpoint); | ||
} | ||
if (props.token) { | ||
script.setAttribute('data-token', props.token); | ||
} | ||
if (isDevelopment() && props.debug === false) { | ||
script.setAttribute('data-debug', 'false'); | ||
} | ||
|
||
script.onerror = (): void => { | ||
const errorMessage = isDevelopment() | ||
? 'Please check if any ad blockers are enabled and try again.' | ||
: 'Be sure to enable Speed Insights for your project and deploy again. See https://vercel.com/docs/speed-insights for more information.'; | ||
|
||
// eslint-disable-next-line no-console -- Logging is okay here | ||
console.log( | ||
`[Vercel Speed Insights] Failed to load script from ${src}. ${errorMessage}`, | ||
); | ||
}; | ||
|
||
document.head.appendChild(script); | ||
|
||
return { | ||
setDynamicPath: (path: string): void => { | ||
script.dataset.dynamicPath = path; | ||
}, | ||
}; | ||
} | ||
|
||
export { inject }; | ||
export type { SpeedInsightsProps }; | ||
|
||
// eslint-disable-next-line import/no-default-export -- Allow default export | ||
export default { | ||
inject, | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,52 +1,14 @@ | ||
import { useCallback, useEffect, useRef } from 'react'; | ||
import type { Metric, MetricWithAttribution } from 'web-vitals'; | ||
import { sendVitals, watchMetrics } from '../generic'; | ||
import type { CollectedMetric } from '../types'; | ||
import React from 'react'; | ||
import { SpeedInsights as SpeedInsightsScript } from '../react'; | ||
import type { SpeedInsightsProps } from '../types'; | ||
import { useDynamicPath } from './utils'; | ||
|
||
interface SpeedInsightsProps { | ||
token: string; | ||
sampleRate?: number; // Only send a percentage of events to the server to reduce costs | ||
} | ||
|
||
export function SpeedInsights({ token, sampleRate }: SpeedInsightsProps): null { | ||
export function SpeedInsights( | ||
props: Omit<SpeedInsightsProps, 'dynamicPath'>, | ||
): JSX.Element { | ||
const dynamicPath = useDynamicPath(); | ||
const vitals = useRef<CollectedMetric[]>([]); | ||
|
||
const flush = useCallback(() => { | ||
if (vitals.current.length > 0) { | ||
if (sampleRate && Math.random() > sampleRate) { | ||
return; | ||
} | ||
const body = vitals.current; | ||
|
||
// eslint-disable-next-line no-console -- ok for now | ||
console.log('flushing', body); | ||
sendVitals(body, token); | ||
|
||
vitals.current = []; | ||
} | ||
}, [sampleRate, vitals.current]); | ||
|
||
useEffect(() => { | ||
addEventListener('visibilitychange', flush); | ||
addEventListener('pagehide', flush); | ||
return () => { | ||
removeEventListener('visibilitychange', flush); | ||
removeEventListener('pagehide', flush); | ||
}; | ||
}, [flush]); | ||
|
||
const reportVital = useCallback( | ||
(metric: MetricWithAttribution): void => { | ||
vitals.current.push({ ...metric, dynamicPath }); | ||
}, | ||
[dynamicPath], | ||
return ( | ||
<SpeedInsightsScript {...(dynamicPath && { dynamicPath })} {...props} /> | ||
); | ||
|
||
useEffect(() => { | ||
watchMetrics(reportVital as (metric: Metric) => void); // TODO: fix typing -- caused by not properly typed library | ||
}, []); | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export const initQueue = (): void => { | ||
// initialize va until script is loaded | ||
if (window.si) return; | ||
|
||
window.si = function a(...params): void { | ||
(window.siq = window.siq || []).push(params); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
'use client'; | ||
import { useEffect, useRef } from 'react'; | ||
import type { SpeedInsightsProps } from '../types'; | ||
import { inject } from '../generic'; | ||
|
||
export function SpeedInsights(props: SpeedInsightsProps): JSX.Element | null { | ||
const scriptDynamicPath = useRef<((path: string) => void) | null>(null); | ||
useEffect(() => { | ||
const script = inject(props); | ||
|
||
scriptDynamicPath.current = script?.setDynamicPath || null; | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (props.dynamicPath && scriptDynamicPath.current) { | ||
scriptDynamicPath.current(props.dynamicPath); | ||
} | ||
}, [props.dynamicPath]); | ||
|
||
return null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,44 @@ | ||
import type { MetricWithAttribution } from 'web-vitals'; | ||
|
||
// Internal typings | ||
export type CollectedMetric = MetricWithAttribution & { | ||
dynamicPath: string | null; | ||
}; | ||
|
||
// V2 | ||
export interface SpeedInsightsPayload { | ||
dsn: string; | ||
speed: string; | ||
metrics: SpeedInsightsMetric[]; | ||
export interface SpeedInsightsProps { | ||
token?: string; | ||
sampleRate?: number; // Only send a percentage of events to the server to reduce costs | ||
beforeSend?: BeforeSendMiddleware; | ||
debug?: boolean; | ||
dynamicPath?: string; | ||
|
||
scriptSrc?: string; | ||
endpoint?: string; | ||
} | ||
|
||
export type EventTypes = 'vital'; | ||
|
||
export interface Event { | ||
type: EventTypes; | ||
url: string; | ||
} | ||
|
||
export type BeforeSendMiddleware = ( | ||
data: Event, | ||
// Should we be more strict here? Compiler won't help a lot if it's that loose | ||
) => Event | null | undefined | false; | ||
|
||
export interface Functions { | ||
beforeSend?: BeforeSendMiddleware; | ||
} | ||
|
||
export interface SpeedInsightsMetric { | ||
id: string; | ||
type: string; | ||
value: string | number; | ||
dynamicPath: string | null; | ||
href: string; | ||
attribution: { | ||
target?: string; | ||
}; | ||
export interface SpeedInsights<T extends keyof Functions = keyof Functions> { | ||
queue: [T, Functions[T]][]; | ||
addAction: (action: T, data: Functions[T]) => void; | ||
} | ||
|
||
declare global { | ||
interface Window { | ||
// Base interface | ||
/** Base interface to track events */ | ||
si?: SpeedInsights['addAction']; | ||
/** Queue for speed insights datapoints, before the library is loaded */ | ||
siq?: SpeedInsights['queue']; | ||
|
||
sil?: boolean; | ||
// vam?: Mode; | ||
} | ||
} |
Oops, something went wrong.