"use client"; import { forwardRef } from "react"; import NextJsImageImport, { type ImageLoaderProps, type ImageProps as NextImageProps, } from "next/image"; import { cx } from "@shared/utils"; // Handle CJS/ESM interop: when bundled as ESM and consumed by Webpack, // the default import may resolve to { default: Component } (or be doubly // wrapped) instead of the function. Unwrap until we land on a callable. const resolveDefaultExport = (mod: unknown): unknown => { let current: any = mod; // Limit iterations to avoid pathological cycles. for (let i = 0; i < 5; i++) { if (typeof current === "function") return current; if (current && typeof current === "object" && "default" in current) { current = current.default; continue; } return current; } return current; }; const NextJsImage = resolveDefaultExport( NextJsImageImport ) as typeof NextJsImageImport; export interface NextImageComponentProps extends NextImageProps { className?: string; } /** * Image loader that uses Contentful's Image API to serve optimized WebP images * at the requested width and quality, avoiding an extra round-trip through * the Next.js image optimization server. */ const contentfulImageLoader = ({ src, width, quality }: ImageLoaderProps) => { const url = new URL(src); url.searchParams.set("w", String(width)); url.searchParams.set("q", String(quality || 90)); url.searchParams.set("fm", "webp"); return url.toString(); }; export const NextImage = forwardRef( ({ className, ...props }, ref) => { const srcString = typeof props.src === "string" ? props.src : ""; const urlWithoutParams = srcString.toLowerCase().split("?")[0] || ""; const isContentfulImage = srcString.includes("images.ctfassets.net"); const isSvgFromContentful = isContentfulImage && urlWithoutParams.endsWith(".svg"); // Use Contentful's Image API for non-SVG Contentful images; // skip optimization entirely for SVGs. const loaderProps = isContentfulImage && !isSvgFromContentful ? { loader: contentfulImageLoader, unoptimized: false } : { unoptimized: isSvgFromContentful }; return ( ); } ); NextImage.displayName = "NextImage";