diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile index 8447937..4e475e8 100644 --- a/apps/web/Dockerfile +++ b/apps/web/Dockerfile @@ -25,9 +25,9 @@ ENV NEXT_PUBLIC_URL=$NEXT_PUBLIC_URL ENV NEXT_PUBLIC_PRODUCT_NAME=$NEXT_PUBLIC_PRODUCT_NAME ENV NEXT_PUBLIC_DEFAULT_LOCALE=$NEXT_PUBLIC_DEFAULT_LOCALE -# TURBOPACK=0 forces webpack for production build — Payload CMS's -# richtext-lexical CSS imports fail under Turbopack. -ENV TURBOPACK=0 +# Node ESM loader that stubs .css imports during route collection. +# Payload CMS deps import .css files that Node can't handle outside webpack. +ENV NODE_OPTIONS="--import ./apps/web/css-stub-loader.mjs" RUN npx turbo run build --filter=web... # Stage 2: runtime — standalone output only diff --git a/apps/web/css-stub-loader.mjs b/apps/web/css-stub-loader.mjs new file mode 100644 index 0000000..743c698 --- /dev/null +++ b/apps/web/css-stub-loader.mjs @@ -0,0 +1,31 @@ +/** + * Node.js ESM custom loader — stubs .css imports as empty modules. + * + * Next.js 16 does route collection in raw Node ESM (not webpack/turbopack). + * Payload CMS dependencies import .css files which Node can't handle. + * This loader intercepts .css resolutions and returns an empty module. + * + * Usage: NODE_OPTIONS="--import ./apps/web/css-stub-loader.mjs" + */ + +import { register } from "node:module"; + +register( + "data:text/javascript," + + encodeURIComponent(` +export function resolve(specifier, context, nextResolve) { + if (specifier.endsWith('.css')) { + return { url: 'data:text/javascript,export default {};', shortCircuit: true }; + } + return nextResolve(specifier, context); +} + +export function load(url, context, nextLoad) { + if (url.endsWith('.css')) { + return { format: 'module', source: 'export default {};', shortCircuit: true }; + } + return nextLoad(url, context); +} +`), + import.meta.url, +); diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 7014e2e..291eeeb 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -89,6 +89,8 @@ const config: NextConfig = { "@payloadcms/db-sqlite", "@payloadcms/richtext-lexical", "@payloadcms/next", + "@payloadcms/ui", + "react-image-crop", "sharp", ], turbopack: {