Files
claudemesh/apps/web/next.config.ts
Alejandro Gutiérrez 3f46a6657a
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
fix(web): add CSS stub loader for Payload CMS route collection in Docker
Node ESM can't handle .css imports during Next.js route collection.
This loader intercepts .css resolutions and returns empty modules,
fixing the build for all Payload deps (richtext-lexical, react-image-crop, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-09 00:35:04 +01:00

140 lines
3.4 KiB
TypeScript

import type { NextConfig } from "next";
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { withPayload } = require("@payloadcms/next/withPayload");
import env from "./env.config";
const INTERNAL_PACKAGES = [
"@turbostarter/analytics-web",
"@turbostarter/api",
"@turbostarter/auth",
"@turbostarter/billing",
"@turbostarter/cms",
"@turbostarter/email",
"@turbostarter/db",
"@turbostarter/i18n",
"@turbostarter/monitoring-web",
"@turbostarter/shared",
"@turbostarter/storage",
"@turbostarter/ui",
"@turbostarter/ui-web",
];
// Security headers for production
const securityHeaders = [
{
key: "X-DNS-Prefetch-Control",
value: "on",
},
{
key: "Strict-Transport-Security",
value: "max-age=63072000; includeSubDomains; preload",
},
{
key: "X-Frame-Options",
value: "SAMEORIGIN",
},
{
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=(), interest-cohort=()",
},
// Content-Security-Policy - configured for development flexibility
// In production, tighten 'unsafe-inline' and 'unsafe-eval' as needed
{
key: "Content-Security-Policy",
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://challenges.cloudflare.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: blob: https: http:",
"font-src 'self' data:",
"connect-src 'self' https://api.stripe.com https://*.sentry.io wss:",
"frame-src 'self' https://js.stripe.com https://hooks.stripe.com https://challenges.cloudflare.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'self'",
"upgrade-insecure-requests",
].join("; "),
},
];
const config: NextConfig = {
reactStrictMode: true,
output: "standalone",
// TEMPORARY: Hono RPC + TanStack Query type inference whack-a-mole blocking production deploy.
// Ship now, fix types post-launch as dedicated tech-debt sprint.
typescript: {
ignoreBuildErrors: true,
},
serverExternalPackages: [
"better-sqlite3",
"@mapbox/node-pre-gyp",
"esbuild",
"payload",
"@payloadcms/db-postgres",
"@payloadcms/db-sqlite",
"@payloadcms/richtext-lexical",
"@payloadcms/next",
"@payloadcms/ui",
"react-image-crop",
"sharp",
],
turbopack: {
rules: {
"*.svg": {
loaders: ["@svgr/webpack"],
as: "*.js",
},
},
},
images: {
remotePatterns: [
{
hostname: "images.unsplash.com",
},
],
},
/** Enables hot reloading for local packages without a build step */
transpilePackages: INTERNAL_PACKAGES,
experimental: {
optimizePackageImports: INTERNAL_PACKAGES,
},
// Apply security headers only in production
// CSP with upgrade-insecure-requests breaks Next.js client-side navigation in dev
async headers() {
if (process.env.NODE_ENV !== "production") {
return [];
}
return [
{
// Apply to all routes
source: "/:path*",
headers: securityHeaders,
},
];
},
};
const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: env.ANALYZE,
});
export default withPayload(withBundleAnalyzer(config));