|
| 1 | +import { fileURLToPath } from 'node:url' |
| 2 | + |
| 3 | +import type { RemotePattern } from 'next-with-adapters/dist/shared/lib/image-config.js' |
| 4 | +import { makeRe } from 'picomatch' |
| 5 | + |
| 6 | +import type { NextConfigComplete, OnBuildCompleteContext } from './types.js' |
| 7 | + |
| 8 | +const NETLIFY_IMAGE_LOADER_FILE = fileURLToPath(import.meta.resolve(`./next-image-loader.cjs`)) |
| 9 | + |
| 10 | +export function modifyConfig(config: NextConfigComplete) { |
| 11 | + if (config.images.loader === 'default') { |
| 12 | + // Set up Netlify Image CDN image's loaderFile |
| 13 | + // see https://nextjs.org/docs/app/api-reference/config/next-config-js/images |
| 14 | + config.images.loader = 'custom' |
| 15 | + config.images.loaderFile = NETLIFY_IMAGE_LOADER_FILE |
| 16 | + } |
| 17 | +} |
| 18 | + |
| 19 | +function generateRegexFromPattern(pattern: string): string { |
| 20 | + return makeRe(pattern).source |
| 21 | +} |
| 22 | + |
| 23 | +export function onBuildComplete(ctx: OnBuildCompleteContext, frameworksAPIConfigArg: any) { |
| 24 | + let frameworksAPIConfig: any = frameworksAPIConfigArg |
| 25 | + |
| 26 | + const { images } = ctx.config |
| 27 | + if (images.loader === 'custom' && images.loaderFile === NETLIFY_IMAGE_LOADER_FILE) { |
| 28 | + const { remotePatterns, domains } = images |
| 29 | + // if Netlify image loader is used, configure allowed remote image sources |
| 30 | + const remoteImageSources: string[] = [] |
| 31 | + if (remotePatterns && remotePatterns.length !== 0) { |
| 32 | + // convert images.remotePatterns to regexes for Frameworks API |
| 33 | + for (const remotePattern of remotePatterns) { |
| 34 | + if (remotePattern instanceof URL) { |
| 35 | + // Note: even if URL notation is used in next.config.js, This will result in RemotePattern |
| 36 | + // object here, so types for the complete config should not have URL as an possible type |
| 37 | + throw new TypeError('Received not supported URL instance in remotePatterns') |
| 38 | + } |
| 39 | + let { protocol, hostname, port, pathname }: RemotePattern = remotePattern |
| 40 | + |
| 41 | + if (pathname) { |
| 42 | + pathname = pathname.startsWith('/') ? pathname : `/${pathname}` |
| 43 | + } |
| 44 | + |
| 45 | + const combinedRemotePattern = `${protocol ?? 'http?(s)'}://${hostname}${ |
| 46 | + port ? `:${port}` : '' |
| 47 | + }${pathname ?? '/**'}` |
| 48 | + |
| 49 | + try { |
| 50 | + remoteImageSources.push(generateRegexFromPattern(combinedRemotePattern)) |
| 51 | + } catch (error) { |
| 52 | + throw new Error( |
| 53 | + `Failed to generate Image CDN remote image regex from Next.js remote pattern: ${JSON.stringify( |
| 54 | + { remotePattern, combinedRemotePattern }, |
| 55 | + null, |
| 56 | + 2, |
| 57 | + )}`, |
| 58 | + { |
| 59 | + cause: error, |
| 60 | + }, |
| 61 | + ) |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + |
| 66 | + if (domains && domains.length !== 0) { |
| 67 | + for (const domain of domains) { |
| 68 | + const patternFromDomain = `http?(s)://${domain}/**` |
| 69 | + try { |
| 70 | + remoteImageSources.push(generateRegexFromPattern(patternFromDomain)) |
| 71 | + } catch (error) { |
| 72 | + throw new Error( |
| 73 | + `Failed to generate Image CDN remote image regex from Next.js domain: ${JSON.stringify( |
| 74 | + { domain, patternFromDomain }, |
| 75 | + null, |
| 76 | + 2, |
| 77 | + )}`, |
| 78 | + { cause: error }, |
| 79 | + ) |
| 80 | + } |
| 81 | + } |
| 82 | + } |
| 83 | + |
| 84 | + if (remoteImageSources.length !== 0) { |
| 85 | + // https://docs.netlify.com/build/frameworks/frameworks-api/#images |
| 86 | + frameworksAPIConfig ??= {} |
| 87 | + frameworksAPIConfig.images ??= {} |
| 88 | + frameworksAPIConfig.images.remote_images = remoteImageSources |
| 89 | + } |
| 90 | + } |
| 91 | + return frameworksAPIConfig |
| 92 | +} |
0 commit comments