add meta pixel integration

This commit is contained in:
Elijah Duffy
2025-12-16 17:21:52 -08:00
parent 1ead8431a7
commit 49c74410a9
4 changed files with 624 additions and 0 deletions

17
src/lib/util/getter.ts Normal file
View File

@@ -0,0 +1,17 @@
/**
* MaybeGetter is a type that can either be a value of type T or a function that returns a value of type T.
* This is useful for cases where you might want to pass a value directly or a function that computes the
* value later, potentially taking advantage of reactivity.
*/
export type MaybeGetter<T> = T | (() => T);
/**
* ResolveGetter returns the underlying value stored by a MaybeGetter type.
* @returns Raw value T or function return T.
*/
export const resolveGetter = <T>(getter: MaybeGetter<T>): T => {
if (typeof getter === 'function') {
return (getter as () => T)();
}
return getter;
};

View File

@@ -0,0 +1,66 @@
import { browser } from '$app/environment';
const SCRIPT_SRC = 'https://connect.facebook.net/en_US/fbevents.js';
type QueuedFBQ = ((...args: unknown[]) => void) & {
queue?: unknown[][];
callMethod?: (...args: unknown[]) => void;
loaded?: boolean;
version?: string;
push?: unknown;
};
/**
* Loads the Meta Pixel script and configures the `fbq` function to queue
* commands until the script is fully loaded. You may optionally await the
* returned Promise to ensure the script has loaded before proceeding.
*/
export const loadMetaPixel = (): Promise<void> => {
// Make sure we're using the browser
if (!browser || !window) {
return Promise.reject(new Error('Window is undefined'));
}
// If fbq is already defined, resolve immediately
const existing = window.fbq as QueuedFBQ | undefined;
if (existing && existing.loaded) {
return Promise.resolve();
}
// Configure fbq to queue commands until Meta takes over
const q = function (...args: unknown[]) {
if (q.callMethod) {
q.callMethod(...args);
} else {
if (!q.queue) q.queue = [];
q.queue.push(args);
}
} as QueuedFBQ;
q.queue = [];
q.push = q;
q.loaded = true;
q.version = '2.0';
window.fbq = q;
return new Promise((resolve, reject) => {
// Avoid adding the same script twice
const existingScript = document.querySelector(
`script[src="${SCRIPT_SRC}"]`
) as HTMLScriptElement | null;
if (existingScript) {
existingScript.addEventListener('load', () => resolve());
existingScript.addEventListener('error', () =>
reject(new Error('Failed to load Meta Pixel script'))
);
return;
}
// Otherwise, create the script element
const script = document.createElement('script');
script.src = SCRIPT_SRC;
script.async = true;
script.addEventListener('load', () => resolve());
script.addEventListener('error', () => reject(new Error('Failed to load Meta Pixel script')));
document.head.appendChild(script);
});
};