diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index f548d8c..5d59df1 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -39,7 +39,7 @@ export default withSentryConfig(nextConfig, { // For all available options, see: // https://www.npmjs.com/package/@sentry/webpack-plugin#options org: "100kode", - project: "javascript-nextjs", + project: "vibe-coding-profiler", // Only print logs for uploading source maps in CI silent: !process.env.CI, diff --git a/apps/web/src/components/PlausibleProvider.tsx b/apps/web/src/components/PlausibleProvider.tsx index 90085d9..5f7964c 100644 --- a/apps/web/src/components/PlausibleProvider.tsx +++ b/apps/web/src/components/PlausibleProvider.tsx @@ -2,7 +2,6 @@ import { Suspense, useEffect, useRef } from "react"; import { usePathname, useSearchParams } from "next/navigation"; -import { init, track } from "@plausible-analytics/tracker"; /** * Inner component that uses useSearchParams (requires Suspense boundary). @@ -11,6 +10,7 @@ function PlausibleTracker() { const pathname = usePathname(); const searchParams = useSearchParams(); const isInitialized = useRef(false); + const initialization = useRef | null>(null); // Initialize Plausible once useEffect(() => { @@ -23,27 +23,24 @@ function PlausibleTracker() { return; } - if (!isInitialized.current) { - init({ - domain, - // Don't track localhost unless explicitly enabled - captureOnLocalhost: process.env.NEXT_PUBLIC_PLAUSIBLE_CAPTURE_LOCALHOST === "true", - // Track outbound link clicks - outboundLinks: true, - // Track file downloads - fileDownloads: true, - // Track form submissions - formSubmissions: true, - // Disable auto capture - we handle it manually for Next.js App Router - autoCapturePageviews: false, - }); - isInitialized.current = true; - } + initialization.current = import("@plausible-analytics/tracker").then(({ init }) => { + if (!isInitialized.current) { + init({ + domain, + captureOnLocalhost: process.env.NEXT_PUBLIC_PLAUSIBLE_CAPTURE_LOCALHOST === "true", + outboundLinks: true, + fileDownloads: true, + formSubmissions: true, + autoCapturePageviews: false, + }); + isInitialized.current = true; + } + }); }, []); // Track page views on route changes useEffect(() => { - if (!process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN || !isInitialized.current) { + if (!process.env.NEXT_PUBLIC_PLAUSIBLE_DOMAIN || !initialization.current) { return; } @@ -53,7 +50,10 @@ function PlausibleTracker() { : pathname; // Track pageview with the current URL - track("pageview", { url }); + void initialization.current.then(async () => { + const { track } = await import("@plausible-analytics/tracker"); + track("pageview", { url }); + }); }, [pathname, searchParams]); return null; diff --git a/apps/web/src/lib/__tests__/analytics.test.ts b/apps/web/src/lib/__tests__/analytics.test.ts new file mode 100644 index 0000000..6b3ee8c --- /dev/null +++ b/apps/web/src/lib/__tests__/analytics.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, it } from "vitest"; + +describe("analytics modules", () => { + it("can load during server rendering without browser globals", async () => { + await expect(import("../analytics")).resolves.toBeDefined(); + await expect(import("../../components/PlausibleProvider")).resolves.toBeDefined(); + }); +}); diff --git a/apps/web/src/lib/analytics.ts b/apps/web/src/lib/analytics.ts index 58f6d18..e112213 100644 --- a/apps/web/src/lib/analytics.ts +++ b/apps/web/src/lib/analytics.ts @@ -1,5 +1,3 @@ -import { track } from "@plausible-analytics/tracker"; - /** * Track a custom event in Plausible Analytics. * @@ -20,10 +18,16 @@ export function trackEvent( return; } - track(eventName, { - props, - interactive: options?.interactive, - revenue: options?.revenue, + if (typeof window === "undefined") { + return; + } + + void import("@plausible-analytics/tracker").then(({ track }) => { + track(eventName, { + props, + interactive: options?.interactive, + revenue: options?.revenue, + }); }); } diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts index c3c54a2..dfcfc1e 100644 --- a/apps/web/vitest.config.ts +++ b/apps/web/vitest.config.ts @@ -10,6 +10,10 @@ export default defineConfig({ resolve: { alias: { "@": resolve(__dirname, "./src"), + "@plausible-analytics/tracker": resolve( + __dirname, + "../../node_modules/@plausible-analytics/tracker/plausible.js" + ), }, }, });