2022-10-21 21:31:53 +02:00
import { existsSync } from 'fs'
import { basename , extname , join , relative , isAbsolute , resolve } from 'path'
2021-10-19 05:46:11 +02:00
import { pathToFileURL } from 'url'
2021-08-03 02:38:42 +02:00
import { Agent as HttpAgent } from 'http'
import { Agent as HttpsAgent } from 'https'
2022-02-14 17:18:57 +01:00
import findUp from 'next/dist/compiled/find-up'
import chalk from '../lib/chalk'
2021-06-30 13:44:40 +02:00
import * as Log from '../build/output/log'
2021-10-16 14:22:42 +02:00
import { CONFIG_FILES , PHASE_DEVELOPMENT_SERVER } from '../shared/lib/constants'
2021-06-30 13:44:40 +02:00
import { execOnce } from '../shared/lib/utils'
2021-07-12 23:38:57 +02:00
import {
defaultConfig ,
normalizeConfig ,
2022-06-18 13:44:37 +02:00
ExperimentalConfig ,
NextConfigComplete ,
2022-07-13 20:31:55 +02:00
validateConfig ,
2022-09-27 22:37:28 +02:00
NextConfig ,
2021-07-12 23:38:57 +02:00
} from './config-shared'
2021-02-27 07:19:35 +01:00
import { loadWebpackHook } from './config-utils'
2022-02-22 15:27:18 +01:00
import {
ImageConfig ,
imageConfigDefault ,
VALID_LOADERS ,
} from '../shared/lib/image-config'
2021-03-08 17:53:52 +01:00
import { loadEnvConfig } from '@next/env'
2022-09-27 22:37:28 +02:00
import { gte as semverGte } from 'next/dist/compiled/semver'
2022-10-22 03:08:37 +02:00
import { getDependencies } from '../lib/get-package-version'
2018-06-04 11:38:46 +02:00
2021-07-21 18:12:33 +02:00
export { DomainLocale , NextConfig , normalizeConfig } from './config-shared'
2021-02-27 07:19:35 +01:00
2022-10-30 16:52:35 +01:00
const NODE_16_VERSION = '16.8.0'
const NODE_18_VERSION = '18.0.0'
const isAboveNodejs16 = semverGte ( process . version , NODE_16_VERSION )
const isAboveNodejs18 = semverGte ( process . version , NODE_18_VERSION )
2022-06-16 21:59:47 +02:00
const experimentalWarning = execOnce (
( configFileName : string , features : string [ ] ) = > {
const s = features . length > 1 ? 's' : ''
Log . warn (
chalk . bold (
` You have enabled experimental feature ${ s } ( ${ features . join (
', '
) } ) in $ { configFileName } . `
)
)
Log . warn (
` Experimental features are not covered by semver, and may cause unexpected or broken application behavior. ` +
` Use at your own risk. `
)
2022-10-25 01:21:28 +02:00
if ( features . includes ( 'appDir' ) ) {
Log . info (
` Thank you for testing \` appDir \` please leave your feedback at https://nextjs.link/app-feedback `
)
}
2022-06-16 21:59:47 +02:00
console . warn ( )
}
)
2019-07-16 13:07:08 +02:00
2022-10-30 16:52:35 +01:00
export function setHttpClientAndAgentOptions ( config : NextConfig ) {
if ( isAboveNodejs16 ) {
2022-11-03 22:30:57 +01:00
if (
config . experimental ? . enableUndici &&
! config . experimental ? . appDir &&
isAboveNodejs18
) {
2022-09-27 22:37:28 +02:00
Log . warn (
2022-10-30 16:52:35 +01:00
` \` enableUndici \` option is unnecessary in Node.js v ${ NODE_18_VERSION } or greater. `
2022-09-27 22:37:28 +02:00
)
2022-10-01 02:20:20 +02:00
} else {
2022-10-30 16:52:35 +01:00
// When appDir is enabled undici is the default because of Response.clone() issues in node-fetch
2022-11-03 01:19:59 +01:00
; ( global as any ) . __NEXT_USE_UNDICI = config . experimental ? . enableUndici
2022-09-27 22:37:28 +02:00
}
2022-10-30 16:52:35 +01:00
} else if ( config . experimental ? . enableUndici ) {
2022-09-27 22:37:28 +02:00
Log . warn (
2022-10-30 16:52:35 +01:00
` \` enableUndici \` option requires Node.js v ${ NODE_16_VERSION } or greater. Falling back to \` node-fetch \` `
2022-09-27 22:37:28 +02:00
)
}
2022-08-15 16:29:51 +02:00
if ( ( global as any ) . __NEXT_HTTP_AGENT ) {
// We only need to assign once because we want
2022-09-27 22:37:28 +02:00
// to reuse the same agent for all requests.
2022-08-15 16:29:51 +02:00
return
}
2022-10-30 16:52:35 +01:00
if ( ! config ) {
2022-08-15 16:29:51 +02:00
throw new Error ( 'Expected config.httpAgentOptions to be an object' )
}
2022-10-30 16:52:35 +01:00
; ( global as any ) . __NEXT_HTTP_AGENT_OPTIONS = config . httpAgentOptions
; ( global as any ) . __NEXT_HTTP_AGENT = new HttpAgent ( config . httpAgentOptions )
; ( global as any ) . __NEXT_HTTPS_AGENT = new HttpsAgent ( config . httpAgentOptions )
2022-08-15 16:29:51 +02:00
}
2022-10-22 03:08:37 +02:00
async function setFontLoaderDefaults ( config : NextConfigComplete , dir : string ) {
// Add @next/font loaders by default if they're installed
const hasNextFontDependency = (
await getDependencies ( {
cwd : dir ,
} )
) . dependencies [ '@next/font' ]
if ( hasNextFontDependency ) {
const googleFontLoader = {
loader : '@next/font/google' ,
}
const localFontLoader = {
loader : '@next/font/local' ,
}
if ( ! config . experimental ) {
config . experimental = { }
}
if ( ! config . experimental . fontLoaders ) {
config . experimental . fontLoaders = [ ]
}
if (
! config . experimental . fontLoaders . find (
2022-10-31 16:56:37 +01:00
( { loader } : any ) = > loader === googleFontLoader . loader
2022-10-22 03:08:37 +02:00
)
) {
config . experimental . fontLoaders . push ( googleFontLoader )
}
if (
! config . experimental . fontLoaders . find (
2022-10-31 16:56:37 +01:00
( { loader } : any ) = > loader === localFontLoader . loader
2022-10-22 03:08:37 +02:00
)
) {
config . experimental . fontLoaders . push ( localFontLoader )
}
}
}
2022-10-21 21:31:53 +02:00
function assignDefaults ( dir : string , userConfig : { [ key : string ] : any } ) {
2021-10-22 03:18:57 +02:00
const configFileName = userConfig . configFileName
2020-08-03 16:03:11 +02:00
if ( typeof userConfig . exportTrailingSlash !== 'undefined' ) {
console . warn (
chalk . yellow . bold ( 'Warning: ' ) +
2021-10-22 01:04:40 +02:00
` The "exportTrailingSlash" option has been renamed to "trailingSlash". Please update your ${ configFileName } . `
2020-08-03 16:03:11 +02:00
)
if ( typeof userConfig . trailingSlash === 'undefined' ) {
userConfig . trailingSlash = userConfig . exportTrailingSlash
}
delete userConfig . exportTrailingSlash
}
2020-05-22 18:46:36 +02:00
const config = Object . keys ( userConfig ) . reduce < { [ key : string ] : any } > (
2020-06-01 23:00:22 +02:00
( currentConfig , key ) = > {
2020-05-22 18:46:36 +02:00
const value = userConfig [ key ]
2019-07-16 13:07:08 +02:00
2020-05-22 18:46:36 +02:00
if ( value === undefined || value === null ) {
2020-06-01 23:00:22 +02:00
return currentConfig
2020-01-20 21:35:29 +01:00
}
2022-06-18 13:44:37 +02:00
if ( key === 'experimental' && typeof value === 'object' ) {
const enabledExperiments : ( keyof ExperimentalConfig ) [ ] = [ ]
// defaultConfig.experimental is predefined and will never be undefined
// This is only a type guard for the typescript
if ( defaultConfig . experimental ) {
for ( const featureName of Object . keys (
value
) as ( keyof ExperimentalConfig ) [ ] ) {
2022-10-30 16:52:35 +01:00
const featureValue = value [ featureName ]
if (
featureName === 'appDir' &&
featureValue === true &&
! isAboveNodejs16
) {
throw new Error (
` experimental.appDir requires Node v ${ NODE_16_VERSION } or later. `
)
}
2022-07-13 20:31:55 +02:00
if (
2022-06-18 13:44:37 +02:00
value [ featureName ] !== defaultConfig . experimental [ featureName ]
) {
enabledExperiments . push ( featureName )
}
}
}
if ( enabledExperiments . length > 0 ) {
experimentalWarning ( configFileName , enabledExperiments )
}
2020-01-20 21:35:29 +01:00
}
2019-10-06 13:44:03 +02:00
2020-05-22 18:46:36 +02:00
if ( key === 'distDir' ) {
if ( typeof value !== 'string' ) {
throw new Error (
` Specified distDir is not a string, found type " ${ typeof value } " `
)
}
const userDistDir = value . trim ( )
2020-01-20 23:19:33 +01:00
2020-05-22 18:46:36 +02:00
// don't allow public as the distDir as this is a reserved folder for
// public files
if ( userDistDir === 'public' ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` The 'public' directory is reserved in Next.js and can not be set as the 'distDir'. https://nextjs.org/docs/messages/can-not-output-to-public `
2020-05-22 18:46:36 +02:00
)
}
// make sure distDir isn't an empty string as it can result in the provided
// directory being deleted in development mode
if ( userDistDir . length === 0 ) {
throw new Error (
` Invalid distDir provided, distDir can not be an empty string. Please remove this config or set it to undefined `
)
}
2020-01-20 23:19:33 +01:00
}
2020-05-22 18:46:36 +02:00
if ( key === 'pageExtensions' ) {
if ( ! Array . isArray ( value ) ) {
throw new Error (
` Specified pageExtensions is not an array of strings, found " ${ value } ". Please update this config or remove it. `
)
}
2020-01-20 23:19:33 +01:00
2020-05-22 18:46:36 +02:00
if ( ! value . length ) {
2020-01-20 23:19:33 +01:00
throw new Error (
2020-05-22 18:46:36 +02:00
` Specified pageExtensions is an empty array. Please update it with the relevant extensions or remove it. `
2020-01-20 23:19:33 +01:00
)
}
2020-05-22 18:46:36 +02:00
value . forEach ( ( ext ) = > {
if ( typeof ext !== 'string' ) {
throw new Error (
` Specified pageExtensions is not an array of strings, found " ${ ext } " of type " ${ typeof ext } ". Please update this config or remove it. `
)
}
} )
2019-02-20 14:00:03 +01:00
}
2020-05-22 18:46:36 +02:00
if ( ! ! value && value . constructor === Object ) {
2020-06-01 23:00:22 +02:00
currentConfig [ key ] = {
2020-05-22 18:46:36 +02:00
. . . defaultConfig [ key ] ,
. . . Object . keys ( value ) . reduce < any > ( ( c , k ) = > {
const v = value [ k ]
if ( v !== undefined && v !== null ) {
c [ k ] = v
}
return c
} , { } ) ,
}
} else {
2020-06-01 23:00:22 +02:00
currentConfig [ key ] = value
2020-05-22 18:46:36 +02:00
}
2020-06-01 23:00:22 +02:00
return currentConfig
2020-05-22 18:46:36 +02:00
} ,
{ }
)
const result = { . . . defaultConfig , . . . config }
2019-12-16 16:07:03 +01:00
if ( typeof result . assetPrefix !== 'string' ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified assetPrefix is not a string, found type " ${ typeof result . assetPrefix } " https://nextjs.org/docs/messages/invalid-assetprefix `
2019-12-16 16:07:03 +01:00
)
}
2020-09-25 04:29:13 +02:00
if ( typeof result . basePath !== 'string' ) {
throw new Error (
` Specified basePath is not a string, found type " ${ typeof result . basePath } " `
)
}
2022-11-03 01:19:59 +01:00
if ( result . experimental ? . appDir ) {
result . experimental . enableUndici = true
}
2020-09-25 04:29:13 +02:00
if ( result . basePath !== '' ) {
if ( result . basePath === '/' ) {
2019-12-29 20:03:12 +01:00
throw new Error (
2020-09-25 04:29:13 +02:00
` Specified basePath /. basePath has to be either an empty string or a path prefix" `
2019-12-29 20:03:12 +01:00
)
}
2020-09-25 04:29:13 +02:00
if ( ! result . basePath . startsWith ( '/' ) ) {
throw new Error (
` Specified basePath has to start with a /, found " ${ result . basePath } " `
)
}
2019-12-29 20:03:12 +01:00
2020-09-25 04:29:13 +02:00
if ( result . basePath !== '/' ) {
if ( result . basePath . endsWith ( '/' ) ) {
2019-12-29 20:03:12 +01:00
throw new Error (
2020-09-25 04:29:13 +02:00
` Specified basePath should not end with /, found " ${ result . basePath } " `
2019-12-29 20:03:12 +01:00
)
}
2020-09-25 04:29:13 +02:00
if ( result . assetPrefix === '' ) {
result . assetPrefix = result . basePath
}
2020-08-08 15:15:00 +02:00
2021-07-12 23:38:57 +02:00
if ( result . amp ? . canonicalBase === '' ) {
2020-09-25 04:29:13 +02:00
result . amp . canonicalBase = result . basePath
2019-12-29 20:03:12 +01:00
}
}
2019-12-10 21:21:52 +01:00
}
2020-09-25 04:29:13 +02:00
2020-10-16 13:10:01 +02:00
if ( result ? . images ) {
2021-09-09 00:03:52 +02:00
const images : ImageConfig = result . images
2020-10-20 18:43:24 +02:00
2020-10-16 13:10:01 +02:00
if ( typeof images !== 'object' ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images should be an object received ${ typeof images } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-16 13:10:01 +02:00
)
}
2020-10-27 15:29:52 +01:00
2020-10-16 13:10:01 +02:00
if ( images . domains ) {
if ( ! Array . isArray ( images . domains ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.domains should be an Array received ${ typeof images . domains } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-16 13:10:01 +02:00
)
}
2020-10-26 16:42:44 +01:00
2021-09-24 01:26:51 +02:00
// static images are automatically prefixed with assetPrefix
// so we need to ensure _next/image allows downloading from
// this resource
if ( config . assetPrefix ? . startsWith ( 'http' ) ) {
images . domains . push ( new URL ( config . assetPrefix ) . hostname )
}
2020-10-26 16:42:44 +01:00
if ( images . domains . length > 50 ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.domains exceeds length of 50, received length ( ${ images . domains . length } ), please reduce the length of the array to continue. \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-26 16:42:44 +01:00
)
}
2020-10-16 13:10:01 +02:00
const invalid = images . domains . filter (
( d : unknown ) = > typeof d !== 'string'
)
if ( invalid . length > 0 ) {
throw new Error (
` Specified images.domains should be an Array of strings received invalid values ( ${ invalid . join (
', '
2021-03-29 10:25:00 +02:00
) } ) . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
2020-10-16 13:10:01 +02:00
)
}
}
2022-05-05 04:19:16 +02:00
2022-09-01 00:44:17 +02:00
const remotePatterns = result ? . images ? . remotePatterns
2022-05-05 04:19:16 +02:00
if ( remotePatterns ) {
if ( ! Array . isArray ( remotePatterns ) ) {
throw new Error (
` Specified images.remotePatterns should be an Array received ${ typeof remotePatterns } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
)
}
if ( remotePatterns . length > 50 ) {
throw new Error (
` Specified images.remotePatterns exceeds length of 50, received length ( ${ remotePatterns . length } ), please reduce the length of the array to continue. \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
)
}
const validProps = new Set ( [ 'protocol' , 'hostname' , 'pathname' , 'port' ] )
const requiredProps = [ 'hostname' ]
const invalidPatterns = remotePatterns . filter (
( d : unknown ) = >
! d ||
typeof d !== 'object' ||
Object . entries ( d ) . some (
( [ k , v ] ) = > ! validProps . has ( k ) || typeof v !== 'string'
) ||
requiredProps . some ( ( k ) = > ! ( k in d ) )
)
if ( invalidPatterns . length > 0 ) {
throw new Error (
` Invalid images.remotePatterns values: \ n ${ invalidPatterns
. map ( ( item ) = > JSON . stringify ( item ) )
. join (
'\n'
) } \ n \ nremotePatterns value must follow format { protocol : 'https' , hostname : 'example.com' , port : '' , pathname : '/imgs/**' } . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
)
}
}
2020-10-26 21:07:52 +01:00
if ( images . deviceSizes ) {
const { deviceSizes } = images
if ( ! Array . isArray ( deviceSizes ) ) {
2020-10-16 13:10:01 +02:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.deviceSizes should be an Array received ${ typeof deviceSizes } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-16 13:10:01 +02:00
)
}
2020-10-26 16:42:44 +01:00
2020-10-26 21:07:52 +01:00
if ( deviceSizes . length > 25 ) {
2020-10-26 16:42:44 +01:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.deviceSizes exceeds length of 25, received length ( ${ deviceSizes . length } ), please reduce the length of the array to continue. \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-26 16:42:44 +01:00
)
}
2020-10-26 21:07:52 +01:00
const invalid = deviceSizes . filter ( ( d : unknown ) = > {
2020-10-26 16:42:44 +01:00
return typeof d !== 'number' || d < 1 || d > 10000
} )
2020-10-16 13:10:01 +02:00
if ( invalid . length > 0 ) {
throw new Error (
2020-10-26 21:07:52 +01:00
` Specified images.deviceSizes should be an Array of numbers that are between 1 and 10000, received invalid values ( ${ invalid . join (
', '
2021-03-29 10:25:00 +02:00
) } ) . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
2020-10-26 21:07:52 +01:00
)
}
}
2020-10-27 14:19:23 +01:00
if ( images . imageSizes ) {
const { imageSizes } = images
if ( ! Array . isArray ( imageSizes ) ) {
2020-10-26 21:07:52 +01:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.imageSizes should be an Array received ${ typeof imageSizes } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-26 21:07:52 +01:00
)
}
2020-10-27 14:19:23 +01:00
if ( imageSizes . length > 25 ) {
2020-10-26 21:07:52 +01:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified images.imageSizes exceeds length of 25, received length ( ${ imageSizes . length } ), please reduce the length of the array to continue. \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2020-10-26 21:07:52 +01:00
)
}
2020-10-27 14:19:23 +01:00
const invalid = imageSizes . filter ( ( d : unknown ) = > {
2020-10-26 21:07:52 +01:00
return typeof d !== 'number' || d < 1 || d > 10000
} )
if ( invalid . length > 0 ) {
throw new Error (
2020-10-27 14:19:23 +01:00
` Specified images.imageSizes should be an Array of numbers that are between 1 and 10000, received invalid values ( ${ invalid . join (
2020-10-16 13:10:01 +02:00
', '
2021-03-29 10:25:00 +02:00
) } ) . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
2020-10-16 13:10:01 +02:00
)
}
}
2020-10-27 15:29:52 +01:00
2020-11-12 20:24:08 +01:00
if ( ! images . loader ) {
images . loader = 'default'
}
if ( ! VALID_LOADERS . includes ( images . loader ) ) {
throw new Error (
` Specified images.loader should be one of ( ${ VALID_LOADERS . join (
', '
) } ) , received invalid value ( $ {
images . loader
2021-03-29 10:25:00 +02:00
} ) . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
2020-11-12 20:24:08 +01:00
)
}
2021-10-20 02:17:24 +02:00
if (
images . loader !== 'default' &&
images . loader !== 'custom' &&
2021-11-02 01:00:47 +01:00
images . path === imageConfigDefault . path
2021-10-20 02:17:24 +02:00
) {
throw new Error (
2022-10-21 21:31:53 +02:00
` Specified images.loader property ( ${ images . loader } ) also requires images.path property to be assigned to a URL prefix. \ nSee more info here: https://nextjs.org/docs/api-reference/next/legacy/image#loader-configuration `
2021-10-20 02:17:24 +02:00
)
}
2021-10-07 18:10:26 +02:00
// Append trailing slash for non-default loaders and when trailingSlash is set
2020-10-27 15:29:52 +01:00
if ( images . path ) {
2020-11-12 20:24:08 +01:00
if (
2021-10-07 18:10:26 +02:00
( images . loader !== 'default' &&
images . path [ images . path . length - 1 ] !== '/' ) ||
result . trailingSlash
2020-11-12 20:24:08 +01:00
) {
2020-10-27 15:29:52 +01:00
images . path += '/'
}
}
2020-12-30 02:57:08 +01:00
if ( images . path === imageConfigDefault . path && result . basePath ) {
images . path = ` ${ result . basePath } ${ images . path } `
}
2021-07-15 21:55:12 +02:00
2022-10-21 21:31:53 +02:00
if ( images . loaderFile ) {
if ( images . loader !== 'default' && images . loader !== 'custom' ) {
throw new Error (
` Specified images.loader property ( ${ images . loader } ) cannot be used with images.loaderFile property. Please set images.loader to "custom". `
)
}
const absolutePath = join ( dir , images . loaderFile )
if ( ! existsSync ( absolutePath ) ) {
throw new Error (
` Specified images.loaderFile does not exist at " ${ absolutePath } ". `
)
}
images . loader = 'custom'
images . loaderFile = absolutePath
}
2021-07-15 21:55:12 +02:00
if (
images . minimumCacheTTL &&
( ! Number . isInteger ( images . minimumCacheTTL ) || images . minimumCacheTTL < 0 )
) {
throw new Error (
2022-07-22 03:58:44 +02:00
` Specified images.minimumCacheTTL should be an integer 0 or more received ( ${ images . minimumCacheTTL } ). \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2021-07-15 21:55:12 +02:00
)
}
2021-10-12 01:17:47 +02:00
if ( images . formats ) {
const { formats } = images
if ( ! Array . isArray ( formats ) ) {
throw new Error (
` Specified images.formats should be an Array received ${ typeof formats } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
)
}
if ( formats . length < 1 || formats . length > 2 ) {
throw new Error (
` Specified images.formats must be length 1 or 2, received length ( ${ formats . length } ), please reduce the length of the array to continue. \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
)
}
const invalid = formats . filter ( ( f ) = > {
return f !== 'image/avif' && f !== 'image/webp'
} )
if ( invalid . length > 0 ) {
throw new Error (
` Specified images.formats should be an Array of mime type strings, received invalid values ( ${ invalid . join (
', '
) } ) . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-images-config`
)
}
}
2022-02-16 20:28:22 +01:00
if (
typeof images . dangerouslyAllowSVG !== 'undefined' &&
typeof images . dangerouslyAllowSVG !== 'boolean'
) {
throw new Error (
2022-07-22 03:58:44 +02:00
` Specified images.dangerouslyAllowSVG should be a boolean received ( ${ images . dangerouslyAllowSVG } ). \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2022-02-16 20:28:22 +01:00
)
}
if (
typeof images . contentSecurityPolicy !== 'undefined' &&
typeof images . contentSecurityPolicy !== 'string'
) {
throw new Error (
2022-07-22 03:58:44 +02:00
` Specified images.contentSecurityPolicy should be a string received ( ${ images . contentSecurityPolicy } ). \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2022-06-16 22:20:17 +02:00
)
}
2022-09-01 00:44:17 +02:00
const unoptimized = result ? . images ? . unoptimized
2022-06-16 22:20:17 +02:00
if (
typeof unoptimized !== 'undefined' &&
typeof unoptimized !== 'boolean'
) {
throw new Error (
` Specified images.unoptimized should be a boolean, received ( ${ unoptimized } ). \ nSee more info here: https://nextjs.org/docs/messages/invalid-images-config `
2022-02-16 20:28:22 +01:00
)
}
2020-10-16 13:10:01 +02:00
}
2022-02-10 02:54:28 +01:00
if ( result . experimental && 'relay' in ( result . experimental as any ) ) {
Log . warn (
` \` relay \` has been moved out of \` experimental \` and into \` compiler \` . Please update your ${ configFileName } file accordingly. `
)
result . compiler = result . compiler || { }
result . compiler . relay = ( result . experimental as any ) . relay
}
if (
result . experimental &&
'styledComponents' in ( result . experimental as any )
) {
Log . warn (
` \` styledComponents \` has been moved out of \` experimental \` and into \` compiler \` . Please update your ${ configFileName } file accordingly. `
)
result . compiler = result . compiler || { }
result . compiler . styledComponents = (
result . experimental as any
) . styledComponents
}
2022-05-21 06:09:30 +02:00
if ( result . experimental && 'emotion' in ( result . experimental as any ) ) {
Log . warn (
` \` emotion \` has been moved out of \` experimental \` and into \` compiler \` . Please update your ${ configFileName } file accordingly. `
)
result . compiler = result . compiler || { }
result . compiler . emotion = ( result . experimental as any ) . emotion
}
2022-02-10 02:54:28 +01:00
if (
result . experimental &&
'reactRemoveProperties' in ( result . experimental as any )
) {
Log . warn (
` \` reactRemoveProperties \` has been moved out of \` experimental \` and into \` compiler \` . Please update your ${ configFileName } file accordingly. `
)
result . compiler = result . compiler || { }
result . compiler . reactRemoveProperties = (
result . experimental as any
) . reactRemoveProperties
}
if ( result . experimental && 'removeConsole' in ( result . experimental as any ) ) {
Log . warn (
` \` removeConsole \` has been moved out of \` experimental \` and into \` compiler \` . Please update your ${ configFileName } file accordingly. `
)
result . compiler = result . compiler || { }
result . compiler . removeConsole = ( result . experimental as any ) . removeConsole
}
2022-05-29 05:53:57 +02:00
if ( result . experimental ? . swcMinifyDebugOptions ) {
Log . warn (
'SWC minify debug option specified. This option is for debugging minifier issues and will be removed once SWC minifier is stable.'
)
}
2022-06-24 21:58:35 +02:00
if ( ( result . experimental as any ) . outputStandalone ) {
Log . warn (
` experimental.outputStandalone has been renamed to "output: 'standalone'", please move the config. `
)
result . output = 'standalone'
}
2021-12-08 00:22:21 +01:00
if (
result . experimental ? . outputFileTracingRoot &&
! isAbsolute ( result . experimental . outputFileTracingRoot )
) {
result . experimental . outputFileTracingRoot = resolve (
result . experimental . outputFileTracingRoot
)
Log . warn (
` experimental.outputFileTracingRoot should be absolute, using: ${ result . experimental . outputFileTracingRoot } `
)
}
2022-06-24 21:58:35 +02:00
if ( result . output === 'standalone' && ! result . outputFileTracing ) {
2021-12-08 00:22:21 +01:00
Log . warn (
2022-06-24 21:58:35 +02:00
` "output: 'standalone'" requires outputFileTracing not be disabled please enable it to leverage the standalone build `
2021-12-08 00:22:21 +01:00
)
2022-06-24 21:58:35 +02:00
result . output = undefined
2021-12-08 00:22:21 +01:00
}
2022-09-27 22:37:28 +02:00
setHttpClientAndAgentOptions ( result || defaultConfig )
2021-08-03 02:38:42 +02:00
2020-10-27 16:30:34 +01:00
if ( result . i18n ) {
const { i18n } = result
2020-10-07 23:11:01 +02:00
const i18nType = typeof i18n
if ( i18nType !== 'object' ) {
2020-10-29 04:48:24 +01:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n should be an object received ${ i18nType } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-29 04:48:24 +01:00
)
2020-10-07 23:11:01 +02:00
}
if ( ! Array . isArray ( i18n . locales ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.locales should be an Array received ${ typeof i18n . locales } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-07 23:11:01 +02:00
)
}
2021-07-27 22:09:36 +02:00
if ( i18n . locales . length > 100 ) {
2021-08-25 15:54:38 +02:00
Log . warn (
` Received ${ i18n . locales . length } i18n.locales items which exceeds the recommended max of 100. \ nSee more info here: https://nextjs.org/docs/advanced-features/i18n-routing#how-does-this-work-with-static-generation `
2021-07-27 22:09:36 +02:00
)
}
2020-10-07 23:11:01 +02:00
const defaultLocaleType = typeof i18n . defaultLocale
if ( ! i18n . defaultLocale || defaultLocaleType !== 'string' ) {
2020-10-29 04:48:24 +01:00
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.defaultLocale should be a string. \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-29 04:48:24 +01:00
)
2020-10-07 23:11:01 +02:00
}
2020-10-10 12:22:45 +02:00
if ( typeof i18n . domains !== 'undefined' && ! Array . isArray ( i18n . domains ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.domains must be an array of domain objects e.g. [ { domain: 'example.fr', defaultLocale: 'fr', locales: ['fr'] } ] received ${ typeof i18n . domains } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-10 12:22:45 +02:00
)
}
if ( i18n . domains ) {
2020-12-04 11:14:55 +01:00
const invalidDomainItems = i18n . domains . filter ( ( item ) = > {
2020-10-10 12:22:45 +02:00
if ( ! item || typeof item !== 'object' ) return true
if ( ! item . defaultLocale ) return true
if ( ! item . domain || typeof item . domain !== 'string' ) return true
2021-07-24 00:16:14 +02:00
const defaultLocaleDuplicate = i18n . domains ? . find (
( altItem ) = >
altItem . defaultLocale === item . defaultLocale &&
altItem . domain !== item . domain
)
if ( defaultLocaleDuplicate ) {
console . warn (
` Both ${ item . domain } and ${ defaultLocaleDuplicate . domain } configured the defaultLocale ${ item . defaultLocale } but only one can. Change one item's default locale to continue `
)
return true
}
2020-10-27 09:45:39 +01:00
let hasInvalidLocale = false
if ( Array . isArray ( item . locales ) ) {
for ( const locale of item . locales ) {
if ( typeof locale !== 'string' ) hasInvalidLocale = true
2020-12-04 11:14:55 +01:00
for ( const domainItem of i18n . domains || [ ] ) {
2020-10-27 09:45:39 +01:00
if ( domainItem === item ) continue
if ( domainItem . locales && domainItem . locales . includes ( locale ) ) {
console . warn (
` Both ${ item . domain } and ${ domainItem . domain } configured the locale ( ${ locale } ) but only one can. Remove it from one i18n.domains config to continue `
)
hasInvalidLocale = true
break
}
}
}
}
return hasInvalidLocale
2020-10-10 12:22:45 +02:00
} )
if ( invalidDomainItems . length > 0 ) {
throw new Error (
` Invalid i18n.domains values: \ n ${ invalidDomainItems
. map ( ( item : any ) = > JSON . stringify ( item ) )
. join (
'\n'
2021-03-29 10:25:00 +02:00
) } \ n \ ndomains value must follow format { domain : 'example.fr' , defaultLocale : 'fr' , locales : [ 'fr' ] } . \ nSee more info here : https : //nextjs.org/docs/messages/invalid-i18n-config`
2020-10-10 12:22:45 +02:00
)
}
}
2020-10-08 13:12:17 +02:00
if ( ! Array . isArray ( i18n . locales ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.locales must be an array of locale strings e.g. ["en-US", "nl-NL"] received ${ typeof i18n . locales } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-08 13:12:17 +02:00
)
}
const invalidLocales = i18n . locales . filter (
( locale : any ) = > typeof locale !== 'string'
)
if ( invalidLocales . length > 0 ) {
throw new Error (
2020-10-29 04:48:24 +01:00
` Specified i18n.locales contains invalid values ( ${ invalidLocales
. map ( String )
. join (
', '
) } ) , locales must be valid locale tags provided as strings e . g . "en-US" . \ n ` +
2020-10-08 13:12:17 +02:00
` See here for list of valid language sub-tags: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry `
)
}
2020-10-07 23:11:01 +02:00
if ( ! i18n . locales . includes ( i18n . defaultLocale ) ) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.defaultLocale should be included in i18n.locales. \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-07 23:11:01 +02:00
)
}
2022-06-07 19:58:19 +02:00
const normalizedLocales = new Set ( )
const duplicateLocales = new Set ( )
i18n . locales . forEach ( ( locale ) = > {
const localeLower = locale . toLowerCase ( )
if ( normalizedLocales . has ( localeLower ) ) {
duplicateLocales . add ( locale )
}
normalizedLocales . add ( localeLower )
} )
if ( duplicateLocales . size > 0 ) {
throw new Error (
` Specified i18n.locales contains the following duplicate locales: \ n ` +
` ${ [ . . . duplicateLocales ] . join ( ', ' ) } \ n ` +
` Each locale should be listed only once. \ n ` +
` See more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
)
}
2020-10-08 13:12:17 +02:00
// make sure default Locale is at the front
i18n . locales = [
i18n . defaultLocale ,
2020-12-04 11:14:55 +01:00
. . . i18n . locales . filter ( ( locale ) = > locale !== i18n . defaultLocale ) ,
2020-10-08 13:12:17 +02:00
]
2020-11-02 22:02:15 +01:00
const localeDetectionType = typeof i18n . localeDetection
2020-10-07 23:11:01 +02:00
if (
localeDetectionType !== 'boolean' &&
localeDetectionType !== 'undefined'
) {
throw new Error (
2021-03-29 10:25:00 +02:00
` Specified i18n.localeDetection should be undefined or a boolean received ${ localeDetectionType } . \ nSee more info here: https://nextjs.org/docs/messages/invalid-i18n-config `
2020-10-07 23:11:01 +02:00
)
}
}
2022-04-16 06:53:00 +02:00
if ( result . devIndicators ? . buildActivityPosition ) {
const { buildActivityPosition } = result . devIndicators
const allowedValues = [
'top-left' ,
'top-right' ,
'bottom-left' ,
'bottom-right' ,
]
if ( ! allowedValues . includes ( buildActivityPosition ) ) {
throw new Error (
` Invalid "devIndicator.buildActivityPosition" provided, expected one of ${ allowedValues . join (
', '
) } , received $ { buildActivityPosition } `
)
}
}
2019-12-10 21:21:52 +01:00
return result
2019-02-20 14:00:03 +01:00
}
2021-02-27 01:29:32 +01:00
export default async function loadConfig (
2019-05-29 13:57:26 +02:00
phase : string ,
dir : string ,
2022-10-25 11:56:26 +02:00
customConfig? : object | null ,
rawConfig? : boolean
2021-07-12 23:38:57 +02:00
) : Promise < NextConfigComplete > {
2021-03-08 17:53:52 +01:00
await loadEnvConfig ( dir , phase === PHASE_DEVELOPMENT_SERVER , Log )
2022-08-09 03:27:42 +02:00
loadWebpackHook ( )
2021-02-27 07:19:35 +01:00
2021-10-22 03:18:57 +02:00
let configFileName = 'next.config.js'
2018-06-04 11:38:46 +02:00
if ( customConfig ) {
2022-10-21 21:31:53 +02:00
return assignDefaults ( dir , {
2021-07-12 23:38:57 +02:00
configOrigin : 'server' ,
2021-10-22 03:18:57 +02:00
configFileName ,
2021-07-12 23:38:57 +02:00
. . . customConfig ,
} ) as NextConfigComplete
2017-05-31 10:06:07 +02:00
}
2021-02-27 01:29:32 +01:00
2021-10-16 14:22:42 +02:00
const path = await findUp ( CONFIG_FILES , { cwd : dir } )
2016-10-28 16:38:24 +02:00
2018-06-04 11:38:46 +02:00
// If config file was found
2020-01-08 17:30:53 +01:00
if ( path ? . length ) {
2021-10-22 03:18:57 +02:00
configFileName = basename ( path )
2021-08-14 15:52:18 +02:00
let userConfigModule : any
try {
2021-10-19 05:46:11 +02:00
// `import()` expects url-encoded strings, so the path must be properly
// escaped and (especially on Windows) absolute paths must pe prefixed
// with the `file://` protocol
2021-12-21 19:52:07 +01:00
if ( process . env . __NEXT_TEST_MODE === 'jest' ) {
// dynamic import does not currently work inside of vm which
// jest relies on so we fall back to require for this case
// https://github.com/nodejs/node/issues/35889
userConfigModule = require ( path )
} else {
userConfigModule = await import ( pathToFileURL ( path ) . href )
}
2022-10-25 11:56:26 +02:00
if ( rawConfig ) {
return userConfigModule
}
2021-08-14 15:52:18 +02:00
} catch ( err ) {
2021-10-22 01:04:40 +02:00
Log . error (
2021-10-22 03:18:57 +02:00
` Failed to load ${ configFileName } , see more info here https://nextjs.org/docs/messages/next-config-error `
2021-08-14 15:52:18 +02:00
)
throw err
}
2022-02-07 09:48:35 +01:00
const userConfig = await normalizeConfig (
2019-05-29 13:57:26 +02:00
phase ,
userConfigModule . default || userConfigModule
)
2020-01-29 09:12:30 +01:00
2022-07-13 20:31:55 +02:00
const validateResult = validateConfig ( userConfig )
if ( validateResult . errors ) {
Log . warn ( ` Invalid next.config.js options detected: ` )
2022-08-03 22:12:16 +02:00
// Only load @segment/ajv-human-errors when invalid config is detected
const { AggregateAjvError } =
require ( 'next/dist/compiled/@segment/ajv-human-errors' ) as typeof import ( 'next/dist/compiled/@segment/ajv-human-errors' )
const aggregatedAjvErrors = new AggregateAjvError ( validateResult . errors , {
fieldLabels : 'js' ,
} )
for ( const error of aggregatedAjvErrors ) {
console . error ( ` - ${ error . message } ` )
}
2022-07-13 20:31:55 +02:00
console . error (
'\nSee more info here: https://nextjs.org/docs/messages/invalid-next-config'
)
}
2020-01-29 09:12:30 +01:00
if ( Object . keys ( userConfig ) . length === 0 ) {
2020-08-04 09:58:23 +02:00
Log . warn (
2021-10-22 03:18:57 +02:00
` Detected ${ configFileName } , no exported configuration found. https://nextjs.org/docs/messages/empty-configuration `
2020-01-29 09:12:30 +01:00
)
}
2021-10-24 17:38:24 +02:00
if ( userConfig . target && userConfig . target !== 'server' ) {
2022-10-18 18:47:13 +02:00
throw new Error (
` The "target" property is no longer supported in ${ configFileName } . \ n ` +
2021-10-24 17:38:24 +02:00
'See more info here https://nextjs.org/docs/messages/deprecated-target-config'
)
}
2020-01-08 17:30:53 +01:00
if ( userConfig . amp ? . canonicalBase ) {
2019-05-29 13:57:26 +02:00
const { canonicalBase } = userConfig . amp || ( { } as any )
2019-05-29 02:32:18 +02:00
userConfig . amp = userConfig . amp || { }
2019-05-29 13:57:26 +02:00
userConfig . amp . canonicalBase =
( canonicalBase . endsWith ( '/' )
? canonicalBase . slice ( 0 , - 1 )
: canonicalBase ) || ''
2019-05-29 02:32:18 +02:00
}
2022-10-22 03:08:37 +02:00
const completeConfig = assignDefaults ( dir , {
2021-10-16 14:22:42 +02:00
configOrigin : relative ( dir , path ) ,
2020-08-24 03:37:48 +02:00
configFile : path ,
2021-10-22 03:18:57 +02:00
configFileName ,
2020-08-24 03:37:48 +02:00
. . . userConfig ,
2021-07-12 23:38:57 +02:00
} ) as NextConfigComplete
2022-10-22 03:08:37 +02:00
await setFontLoaderDefaults ( completeConfig , dir )
return completeConfig
2019-10-30 13:18:33 +01:00
} else {
2021-10-16 14:22:42 +02:00
const configBaseName = basename ( CONFIG_FILES [ 0 ] , extname ( CONFIG_FILES [ 0 ] ) )
2019-10-30 13:18:33 +01:00
const nonJsPath = findUp . sync (
[
` ${ configBaseName } .jsx ` ,
` ${ configBaseName } .ts ` ,
` ${ configBaseName } .tsx ` ,
` ${ configBaseName } .json ` ,
] ,
{ cwd : dir }
)
2020-01-08 17:30:53 +01:00
if ( nonJsPath ? . length ) {
2019-10-30 13:18:33 +01:00
throw new Error (
` Configuring Next.js via ' ${ basename (
nonJsPath
2021-10-16 14:22:42 +02:00
) } ' is not supported. Please replace the file with ' next . config . js ' or ' next . config . mjs ' . `
2019-10-30 13:18:33 +01:00
)
}
2018-06-04 11:38:46 +02:00
}
2017-05-31 10:06:07 +02:00
2022-03-31 04:11:00 +02:00
// always call assignDefaults to ensure settings like
// reactRoot can be updated correctly even with no next.config.js
2022-10-21 21:31:53 +02:00
const completeConfig = assignDefaults (
dir ,
defaultConfig
) as NextConfigComplete
2021-10-22 03:18:57 +02:00
completeConfig . configFileName = configFileName
2022-09-27 22:37:28 +02:00
setHttpClientAndAgentOptions ( completeConfig )
2022-10-22 03:08:37 +02:00
await setFontLoaderDefaults ( completeConfig , dir )
2021-08-03 02:38:42 +02:00
return completeConfig
2016-10-28 16:38:24 +02:00
}