import { withSentryConfig } from '@sentry/nextjs'; import { createJiti } from 'jiti'; import { withPlausibleProxy } from 'next-plausible'; const jiti = createJiti(import.meta.url); const { env } = /** @type {typeof import('./src/env')} */ ( await jiti.import('./src/env') ); const { posthogScriptAliases } = /** @type {typeof import('./src/lib/posthog-scripts')} */ ( await jiti.import('./src/lib/posthog-scripts') ); /** @type {import("next").NextConfig} */ const baseConfig = { output: 'standalone', images: { remotePatterns: [ { protocol: 'https', hostname: '*.gbrown.org', }, ], }, serverExternalPackages: ['require-in-the-middle'], experimental: { serverActions: { bodySizeLimit: '10mb', }, // Resolves `import { Button } from '@gib/ui'` to the button module alone, // so marketing pages never pull in admin-only Radix widgets or Recharts. // radix-ui is the umbrella package the shadcn components import from. // lucide-react, date-fns and @tabler/icons-react are on Next's default list. optimizePackageImports: ['@gib/ui', 'radix-ui'], }, /** Enables hot reloading for local packages without a build step */ transpilePackages: ['@gib/backend', '@gib/ui'], typescript: { ignoreBuildErrors: true }, // PostHog's capture paths end in a slash (/e/, /i/v0/e/), and Next's // built-in trailing-slash redirect would answer those proxied POSTs with a // redirect. It is turned off here and replaced by the redirect below, which // strips trailing slashes everywhere except /ingest. skipTrailingSlashRedirect: true, redirects: async () => [ { source: '/:path((?!ingest(?:/|$)).+)/', destination: '/:path', permanent: true, }, ], // Same-origin proxy for PostHog, so blocklists that match the PostHog host // never see the request. The aliased extension scripts (blocked by file // name, see src/lib/posthog-scripts.ts) map back to their real files first. rewrites: async () => { const host = env.NEXT_PUBLIC_POSTHOG_HOST; if (!host) return []; return [ ...Object.entries(posthogScriptAliases).map(([name, alias]) => ({ source: `/ingest/static/${alias}.js`, destination: `${host}/static/${name}.js`, })), { source: '/ingest/:path*', destination: `${host}/:path*` }, ]; }, }; // Serves Plausible's per-site script from this origin so ad blockers see a // first-party request. next-plausible 4 takes the full script URL. Its default // event path is /api/event, which sits inside Payload's /api routes, so both // paths move under /pa. const config = env.NEXT_PUBLIC_PLAUSIBLE_SRC ? withPlausibleProxy({ src: env.NEXT_PUBLIC_PLAUSIBLE_SRC, scriptPath: '/pa/script.js', apiPath: '/pa/event', })(baseConfig) : baseConfig; const sentryConfig = { // For all available options, see: // https://www.npmjs.com/package/@sentry/webpack-plugin#options org: process.env.NEXT_PUBLIC_SENTRY_ORG, project: process.env.NEXT_PUBLIC_SENTRY_PROJECT_NAME, sentryUrl: process.env.NEXT_PUBLIC_SENTRY_URL, authToken: process.env.SENTRY_AUTH_TOKEN, // Only print logs for uploading source maps in CI silent: !process.env.CI, // For all available options, see: // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ // Upload a larger set of source maps for prettier stack traces (increases build time) widenClientFileUpload: true, // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. // This can increase your server load as well as your hosting bill. // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- // side errors will fail. tunnelRoute: '/monitoring', // Automatically tree-shake Sentry logger statements to reduce bundle size disableLogger: true, // Capture React Component Names reactComponentAnnotation: { enabled: true, }, }; export default withSentryConfig(config, sentryConfig);