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