import type { NextConfig } from "next"; import dotenv from "dotenv"; import path from "path"; import bundleAnalyzer from "@next/bundle-analyzer"; import createNextIntlPlugin from "next-intl/plugin"; // Load the .env file from the working directory dotenv.config({ path: path.resolve(process.cwd(), ".env") }); const nextConfig: NextConfig = { // Enable standalone output for Docker output: "standalone", outputFileTracingRoot: path.join(process.cwd()), // Optimize for production compress: true, poweredByHeader: false, // React Strict Mode reactStrictMode: true, // Disable ESLint during build for Docker eslint: { ignoreDuringBuilds: process.env.NODE_ENV === "production", }, // Environment variables env: { NEXT_PUBLIC_BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, }, // Performance optimizations // NOTE: `optimizePackageImports` can cause dev-time webpack runtime issues with some setups. // Keep it enabled for production builds only. experimental: process.env.NODE_ENV === "production" ? { optimizePackageImports: ["lucide-react", "framer-motion"], } : {}, // Image optimization images: { formats: ["image/webp", "image/avif"], minimumCacheTTL: 60, remotePatterns: [ { protocol: "https", hostname: "i.scdn.co", }, { protocol: "https", hostname: "cdn.discordapp.com", }, { protocol: "https", hostname: "media.discordapp.net", }, ], }, // Webpack configuration webpack: (config) => { // Fix for module resolution issues config.resolve.fallback = { ...config.resolve.fallback, fs: false, net: false, tls: false, }; return config; }, // Security and cache headers async headers() { const csp = process.env.NODE_ENV === "production" ? // Avoid `unsafe-eval` in production (reduces XSS impact and enables stronger CSP) "default-src 'self'; script-src 'self' 'unsafe-inline'; script-src-elem 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" : // Dev CSP: allow eval for tooling compatibility "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src-elem 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; font-src 'self' data:; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';"; return [ { source: "/(.*)", headers: [ { key: "X-DNS-Prefetch-Control", value: "on", }, { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload", }, { key: "X-Frame-Options", value: "DENY", }, { key: "X-Content-Type-Options", value: "nosniff", }, { key: "X-XSS-Protection", value: "1; mode=block", }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin", }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()", }, { key: "Content-Security-Policy", value: csp, }, ], }, { source: "/api/(.*)", headers: [ { key: "Cache-Control", value: "no-store, no-cache, must-revalidate, proxy-revalidate", }, ], }, { source: "/_next/static/(.*)", headers: [ { key: "Cache-Control", // In dev, aggressive caching breaks HMR and can brick a tab with stale chunks. value: process.env.NODE_ENV === "production" ? "public, max-age=31536000, immutable" : "no-store", }, ], }, ]; }, }; const withBundleAnalyzer = bundleAnalyzer({ enabled: process.env.ANALYZE === "true", }); const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); export default withBundleAnalyzer(withNextIntl(nextConfig));