import * as Sentry from '@sentry/vue'; import { defineNuxtPlugin, useRouter } from 'nuxt/app'; import type { RouteLocationNormalized } from 'vue-router'; import { executeUnlessPrerendering } from '~/src/helpers.ts'; const USER_AT = /^\/@.+/; const USER_SUBPAGE = /^\/(u|card)\/.*/; export const normalizeUrl = (page: URL): URL => { if (USER_AT.test(page.pathname)) { page.pathname = page.pathname.replace(USER_AT, '/@--redacted--'); } if (USER_SUBPAGE.test(page.pathname)) { page.pathname = page.pathname.replace(USER_SUBPAGE, '/$1/--redacted--'); } page.search = ''; page.hash = ''; return page; }; export default defineNuxtPlugin((nuxtApp) => { const router = useRouter(); const trackPageview = (route: RouteLocationNormalized): void => { executeUnlessPrerendering(() => { try { const toUrl = normalizeUrl(new URL(route.fullPath, window.location.href)); console.debug('[analytics] tracking page view:', toUrl.toString()); useTrackPageview({ data: { url: toUrl.toString(), }, }); } catch (error) { Sentry.captureException(error); } })(); }; // Track the initial page load nuxtApp.hook('app:mounted', () => { trackPageview(router.currentRoute.value); }); // Track client-side navigation router.afterEach((to: RouteLocationNormalized, from: RouteLocationNormalized) => { const toUrl = normalizeUrl(new URL(to.fullPath, window.location.href)); const fromUrl = normalizeUrl(new URL(from.fullPath, window.location.href)); if (toUrl.toString() === fromUrl.toString()) { // Same page, but different search param return; } trackPageview(to); }); });