2021-12-21 16:13:45 +01:00
import chalk from 'next/dist/compiled/chalk'
2020-04-21 14:21:41 +02:00
import { parse as parseUrl } from 'url'
2021-06-30 13:44:40 +02:00
import { NextConfig } from '../server/config'
2020-12-04 11:14:55 +01:00
import * as pathToRegexp from 'next/dist/compiled/path-to-regexp'
2022-01-03 18:41:50 +01:00
import { escapeStringRegexp } from '../shared/lib/escape-regexp'
2020-01-14 19:28:48 +01:00
import {
PERMANENT_REDIRECT_STATUS ,
TEMPORARY_REDIRECT_STATUS ,
2021-06-30 11:43:31 +02:00
} from '../shared/lib/constants'
2021-09-16 18:06:57 +02:00
import isError from './is-error'
2021-03-24 17:50:16 +01:00
export type RouteHas =
| {
type : 'header' | 'query' | 'cookie'
key : string
value? : string
}
| {
type : 'host'
key? : undefined
value : string
}
2019-12-19 17:48:34 +01:00
2019-12-10 15:54:56 +01:00
export type Rewrite = {
source : string
destination : string
2020-07-12 21:03:49 +02:00
basePath? : false
2020-11-14 04:35:42 +01:00
locale? : false
2021-03-24 17:50:16 +01:00
has? : RouteHas [ ]
2019-12-10 15:54:56 +01:00
}
2020-01-01 13:47:58 +01:00
export type Header = {
source : string
2020-07-12 21:03:49 +02:00
basePath? : false
2020-11-14 04:35:42 +01:00
locale? : false
2020-01-01 13:47:58 +01:00
headers : Array < { key : string ; value : string } >
2021-03-24 17:50:16 +01:00
has? : RouteHas [ ]
2020-01-01 13:47:58 +01:00
}
2020-11-11 08:13:18 +01:00
// internal type used for validation (not user facing)
2021-03-26 16:19:48 +01:00
export type Redirect = {
source : string
destination : string
basePath? : false
locale? : false
has? : RouteHas [ ]
2020-11-11 08:13:18 +01:00
statusCode? : number
permanent? : boolean
}
2020-11-02 22:47:59 +01:00
export const allowedStatusCodes = new Set ( [ 301 , 302 , 303 , 307 , 308 ] )
2021-03-24 17:50:16 +01:00
const allowedHasTypes = new Set ( [ 'header' , 'cookie' , 'query' , 'host' ] )
2021-04-29 19:50:06 +02:00
const namedGroupsRegex = /\(\?<([a-zA-Z][a-zA-Z0-9]*)>/g
2019-12-10 15:54:56 +01:00
2020-11-04 23:18:44 +01:00
export function getRedirectStatus ( route : {
statusCode? : number
permanent? : boolean
} ) : number {
2020-01-14 19:28:48 +01:00
return (
route . statusCode ||
( route . permanent ? PERMANENT_REDIRECT_STATUS : TEMPORARY_REDIRECT_STATUS )
)
}
2020-07-08 20:45:53 +02:00
export function normalizeRouteRegex ( regex : string ) {
// clean up un-necessary escaping from regex.source which turns / into \\/
return regex . replace ( /\\\//g , '/' )
}
2021-07-13 21:38:14 +02:00
// for redirects we restrict matching /_next and for all routes
// we add an optional trailing slash at the end for easier
// configuring between trailingSlash: true/false
export function modifyRouteRegex ( regex : string , restrictedPaths? : string [ ] ) {
if ( restrictedPaths ) {
regex = regex . replace (
/\^/ ,
` ^(?! ${ restrictedPaths
. map ( ( path ) = > path . replace ( /\//g , '\\/' ) )
. join ( '|' ) } ) `
)
}
regex = regex . replace ( /\$$/ , '(?:\\/)?$' )
return regex
}
2021-08-17 09:18:08 +02:00
function checkRedirect ( route : Redirect ) : {
invalidParts : string [ ]
hadInvalidStatus : boolean
} {
2020-01-01 13:47:58 +01:00
const invalidParts : string [ ] = [ ]
let hadInvalidStatus : boolean = false
if ( route . statusCode && ! allowedStatusCodes . has ( route . statusCode ) ) {
hadInvalidStatus = true
invalidParts . push ( ` \` statusCode \` is not undefined or valid statusCode ` )
}
2020-01-14 19:28:48 +01:00
if ( typeof route . permanent !== 'boolean' && ! route . statusCode ) {
invalidParts . push ( ` \` permanent \` is not set to \` true \` or \` false \` ` )
}
2020-01-01 13:47:58 +01:00
return {
invalidParts ,
hadInvalidStatus ,
}
}
2020-05-19 10:59:03 +02:00
function checkHeader ( route : Header ) : string [ ] {
2020-01-01 13:47:58 +01:00
const invalidParts : string [ ] = [ ]
if ( ! Array . isArray ( route . headers ) ) {
invalidParts . push ( '`headers` field must be an array' )
2021-08-25 02:26:33 +02:00
} else if ( route . headers . length === 0 ) {
invalidParts . push ( '`headers` field cannot be empty' )
2020-01-01 13:47:58 +01:00
} else {
for ( const header of route . headers ) {
if ( ! header || typeof header !== 'object' ) {
invalidParts . push (
"`headers` items must be object with { key: '', value: '' }"
)
break
}
if ( typeof header . key !== 'string' ) {
invalidParts . push ( '`key` in header item must be string' )
break
}
if ( typeof header . value !== 'string' ) {
invalidParts . push ( '`value` in header item must be string' )
break
}
}
}
return invalidParts
}
2020-04-21 14:21:41 +02:00
type ParseAttemptResult = {
error? : boolean
tokens? : pathToRegexp.Token [ ]
2021-08-03 00:34:44 +02:00
regexStr? : string
2020-04-21 14:21:41 +02:00
}
function tryParsePath ( route : string , handleUrl? : boolean ) : ParseAttemptResult {
const result : ParseAttemptResult = { }
let routePath = route
try {
if ( handleUrl ) {
const parsedDestination = parseUrl ( route , true )
2020-05-18 21:24:37 +02:00
routePath = ` ${ parsedDestination . pathname ! } ${
parsedDestination . hash || ''
} `
2020-04-21 14:21:41 +02:00
}
// Make sure we can parse the source properly
result . tokens = pathToRegexp . parse ( routePath )
2021-08-03 00:34:44 +02:00
const regex = pathToRegexp . tokensToRegexp ( result . tokens )
result . regexStr = regex . source
2020-04-21 14:21:41 +02:00
} catch ( err ) {
2021-03-29 10:25:00 +02:00
// If there is an error show our error link but still show original error or a formatted one if we can
2021-09-16 18:06:57 +02:00
let errMatches
2020-04-21 14:21:41 +02:00
2021-09-16 18:06:57 +02:00
if ( isError ( err ) && ( errMatches = err . message . match ( /at (\d{0,})/ ) ) ) {
2020-04-21 14:21:41 +02:00
const position = parseInt ( errMatches [ 1 ] , 10 )
console . error (
` \ nError parsing \` ${ route } \` ` +
2021-03-29 10:25:00 +02:00
` https://nextjs.org/docs/messages/invalid-route-source \ n ` +
2020-04-21 14:21:41 +02:00
` Reason: ${ err . message } \ n \ n ` +
` ${ routePath } \ n ` +
` ${ new Array ( position ) . fill ( ' ' ) . join ( '' ) } ^ \ n `
)
} else {
console . error (
2021-03-29 10:25:00 +02:00
` \ nError parsing ${ route } https://nextjs.org/docs/messages/invalid-route-source ` ,
2020-04-21 14:21:41 +02:00
err
)
}
result . error = true
}
return result
}
2020-01-01 13:47:58 +01:00
export type RouteType = 'rewrite' | 'redirect' | 'header'
2020-06-09 22:16:23 +02:00
function checkCustomRoutes (
2020-01-01 13:47:58 +01:00
routes : Redirect [ ] | Header [ ] | Rewrite [ ] ,
type : RouteType
2019-12-10 15:54:56 +01:00
) : void {
2020-02-24 23:01:02 +01:00
if ( ! Array . isArray ( routes ) ) {
2021-04-20 04:03:46 +02:00
console . error (
` Error: ${ type } s must return an array, received ${ typeof routes } . \ n ` +
2021-03-29 10:25:00 +02:00
` See here for more info: https://nextjs.org/docs/messages/routes-must-be-array `
2020-02-24 23:01:02 +01:00
)
2021-04-20 04:03:46 +02:00
process . exit ( 1 )
2020-02-24 23:01:02 +01:00
}
2019-12-10 15:54:56 +01:00
let numInvalidRoutes = 0
let hadInvalidStatus = false
2021-03-24 17:50:16 +01:00
let hadInvalidHas = false
2020-01-01 13:47:58 +01:00
2021-03-26 16:19:48 +01:00
const allowedKeys = new Set < string > ( [ 'source' , 'basePath' , 'locale' , 'has' ] )
if ( type === 'rewrite' ) {
allowedKeys . add ( 'destination' )
}
if ( type === 'redirect' ) {
allowedKeys . add ( 'statusCode' )
allowedKeys . add ( 'permanent' )
allowedKeys . add ( 'destination' )
}
if ( type === 'header' ) {
allowedKeys . add ( 'headers' )
2020-01-01 13:47:58 +01:00
}
2019-12-10 15:54:56 +01:00
for ( const route of routes ) {
2020-01-23 18:08:25 +01:00
if ( ! route || typeof route !== 'object' ) {
console . error (
` The route ${ JSON . stringify (
route
) } is not a valid object with \ ` source \` and \` ${
type === 'header' ? 'headers' : 'destination'
} \ ` `
)
numInvalidRoutes ++
continue
}
2020-08-04 19:50:09 +02:00
if (
type === 'rewrite' &&
( route as Rewrite ) . basePath === false &&
! (
( route as Rewrite ) . destination . startsWith ( 'http://' ) ||
( route as Rewrite ) . destination . startsWith ( 'https://' )
)
) {
console . error (
` The route ${
( route as Rewrite ) . source
2021-03-29 10:25:00 +02:00
} rewrites urls outside of the basePath . Please use a destination that starts with \ ` http:// \` or \` https:// \` https://nextjs.org/docs/messages/invalid-external-rewrite `
2020-08-04 19:50:09 +02:00
)
numInvalidRoutes ++
continue
}
2019-12-10 15:54:56 +01:00
const keys = Object . keys ( route )
2020-05-18 21:24:37 +02:00
const invalidKeys = keys . filter ( ( key ) = > ! allowedKeys . has ( key ) )
2020-01-01 13:47:58 +01:00
const invalidParts : string [ ] = [ ]
2019-12-10 15:54:56 +01:00
2020-07-12 21:03:49 +02:00
if ( typeof route . basePath !== 'undefined' && route . basePath !== false ) {
invalidParts . push ( '`basePath` must be undefined or false' )
}
2020-11-14 04:35:42 +01:00
if ( typeof route . locale !== 'undefined' && route . locale !== false ) {
2020-11-19 17:04:55 +01:00
invalidParts . push ( '`locale` must be undefined or false' )
2020-11-14 04:35:42 +01:00
}
2021-03-24 17:50:16 +01:00
if ( typeof route . has !== 'undefined' && ! Array . isArray ( route . has ) ) {
invalidParts . push ( '`has` must be undefined or valid has object' )
hadInvalidHas = true
} else if ( route . has ) {
const invalidHasItems = [ ]
for ( const hasItem of route . has ) {
let invalidHasParts = [ ]
if ( ! allowedHasTypes . has ( hasItem . type ) ) {
invalidHasParts . push ( ` invalid type " ${ hasItem . type } " ` )
}
if ( typeof hasItem . key !== 'string' && hasItem . type !== 'host' ) {
invalidHasParts . push ( ` invalid key " ${ hasItem . key } " ` )
}
if (
typeof hasItem . value !== 'undefined' &&
typeof hasItem . value !== 'string'
) {
invalidHasParts . push ( ` invalid value " ${ hasItem . value } " ` )
}
if ( typeof hasItem . value === 'undefined' && hasItem . type === 'host' ) {
invalidHasParts . push ( ` value is required for "host" type ` )
}
if ( invalidHasParts . length > 0 ) {
invalidHasItems . push (
` ${ invalidHasParts . join ( ', ' ) } for ${ JSON . stringify ( hasItem ) } `
)
}
}
if ( invalidHasItems . length > 0 ) {
hadInvalidHas = true
const itemStr = ` item ${ invalidHasItems . length === 1 ? '' : 's' } `
console . error (
` Invalid \` has \` ${ itemStr } : \ n ` + invalidHasItems . join ( '\n' )
)
console . error ( )
invalidParts . push ( ` invalid \` has \` ${ itemStr } found ` )
}
}
2019-12-10 15:54:56 +01:00
if ( ! route . source ) {
invalidParts . push ( '`source` is missing' )
} else if ( typeof route . source !== 'string' ) {
invalidParts . push ( '`source` is not a string' )
} else if ( ! route . source . startsWith ( '/' ) ) {
invalidParts . push ( '`source` does not start with /' )
}
2020-01-01 13:47:58 +01:00
if ( type === 'header' ) {
invalidParts . push ( . . . checkHeader ( route as Header ) )
} else {
let _route = route as Rewrite | Redirect
if ( ! _route . destination ) {
invalidParts . push ( '`destination` is missing' )
} else if ( typeof _route . destination !== 'string' ) {
invalidParts . push ( '`destination` is not a string' )
2020-02-04 20:08:03 +01:00
} else if (
type === 'rewrite' &&
! _route . destination . match ( /^(\/|https:\/\/|http:\/\/)/ )
) {
invalidParts . push (
'`destination` does not start with `/`, `http://`, or `https://`'
)
2020-01-01 13:47:58 +01:00
}
2019-12-10 15:54:56 +01:00
}
2020-01-01 13:47:58 +01:00
if ( type === 'redirect' ) {
const result = checkRedirect ( route as Redirect )
2020-01-14 19:28:48 +01:00
hadInvalidStatus = hadInvalidStatus || result . hadInvalidStatus
2020-01-01 13:47:58 +01:00
invalidParts . push ( . . . result . invalidParts )
2019-12-10 15:54:56 +01:00
}
2020-03-10 21:09:35 +01:00
let sourceTokens : pathToRegexp.Token [ ] | undefined
2020-01-22 11:16:13 +01:00
if ( typeof route . source === 'string' && route . source . startsWith ( '/' ) ) {
2020-01-01 13:47:58 +01:00
// only show parse error if we didn't already show error
// for not being a string
2021-08-03 00:34:44 +02:00
const { tokens , error , regexStr } = tryParsePath ( route . source )
2020-04-21 14:21:41 +02:00
if ( error ) {
2020-01-22 11:16:13 +01:00
invalidParts . push ( '`source` parse failed' )
2020-01-01 13:47:58 +01:00
}
2021-08-03 00:34:44 +02:00
if ( regexStr && regexStr . length > 4096 ) {
invalidParts . push ( '`source` exceeds max built length of 4096' )
}
2020-04-21 14:21:41 +02:00
sourceTokens = tokens
2019-12-19 17:48:34 +01:00
}
2021-04-01 11:15:28 +02:00
const hasSegments = new Set < string > ( )
if ( route . has ) {
for ( const hasItem of route . has ) {
if ( ! hasItem . value && hasItem . key ) {
hasSegments . add ( hasItem . key )
}
if ( hasItem . value ) {
2021-04-29 19:50:06 +02:00
for ( const match of hasItem . value . matchAll ( namedGroupsRegex ) ) {
if ( match [ 1 ] ) {
hasSegments . add ( match [ 1 ] )
}
}
2021-04-13 14:34:51 +02:00
if ( hasItem . type === 'host' ) {
hasSegments . add ( 'host' )
2021-04-01 11:15:28 +02:00
}
}
}
}
2019-12-19 17:48:34 +01:00
2020-03-10 21:09:35 +01:00
// make sure no unnamed patterns are attempted to be used in the
// destination as this can cause confusion and is not allowed
if ( typeof ( route as Rewrite ) . destination === 'string' ) {
if (
( route as Rewrite ) . destination . startsWith ( '/' ) &&
Array . isArray ( sourceTokens )
) {
const unnamedInDest = new Set ( )
for ( const token of sourceTokens ) {
if ( typeof token === 'object' && typeof token . name === 'number' ) {
2020-04-21 14:21:41 +02:00
const unnamedIndex = new RegExp ( ` : ${ token . name } (?! \\ d) ` )
if ( ( route as Rewrite ) . destination . match ( unnamedIndex ) ) {
unnamedInDest . add ( ` : ${ token . name } ` )
2020-03-10 21:09:35 +01:00
}
}
}
if ( unnamedInDest . size > 0 ) {
invalidParts . push (
` \` destination \` has unnamed params ${ [ . . . unnamedInDest ] . join (
', '
) } `
)
2020-04-21 14:21:41 +02:00
} else {
const {
tokens : destTokens ,
2021-08-03 00:34:44 +02:00
regexStr : destRegexStr ,
2020-04-21 14:21:41 +02:00
error : destinationParseFailed ,
} = tryParsePath ( ( route as Rewrite ) . destination , true )
2021-08-03 00:34:44 +02:00
if ( destRegexStr && destRegexStr . length > 4096 ) {
invalidParts . push ( '`destination` exceeds max built length of 4096' )
}
2020-04-21 14:21:41 +02:00
if ( destinationParseFailed ) {
invalidParts . push ( '`destination` parse failed' )
} else {
const sourceSegments = new Set (
sourceTokens
2020-05-18 21:24:37 +02:00
. map ( ( item ) = > typeof item === 'object' && item . name )
2020-04-21 14:21:41 +02:00
. filter ( Boolean )
)
const invalidDestSegments = new Set ( )
for ( const token of destTokens ! ) {
if (
typeof token === 'object' &&
2021-04-01 11:15:28 +02:00
! sourceSegments . has ( token . name ) &&
! hasSegments . has ( token . name as string )
2020-04-21 14:21:41 +02:00
) {
invalidDestSegments . add ( token . name )
}
}
if ( invalidDestSegments . size ) {
invalidParts . push (
2021-04-01 11:15:28 +02:00
` \` destination \` has segments not in \` source \` or \` has \` ( ${ [
2020-04-21 14:21:41 +02:00
. . . invalidDestSegments ,
] . join ( ', ' ) } ) `
)
}
}
2020-03-10 21:09:35 +01:00
}
}
}
2019-12-10 15:54:56 +01:00
const hasInvalidKeys = invalidKeys . length > 0
const hasInvalidParts = invalidParts . length > 0
if ( hasInvalidKeys || hasInvalidParts ) {
console . error (
` ${ invalidParts . join ( ', ' ) } ${
invalidKeys . length
? ( hasInvalidParts ? ',' : '' ) +
` invalid field ${ invalidKeys . length === 1 ? '' : 's' } : ` +
invalidKeys . join ( ',' )
: ''
} for route $ { JSON . stringify ( route ) } `
)
2021-03-24 17:50:16 +01:00
console . error ( )
2019-12-10 15:54:56 +01:00
numInvalidRoutes ++
}
}
if ( numInvalidRoutes > 0 ) {
if ( hadInvalidStatus ) {
console . error (
` \ nValid redirect statusCode values are ${ [ . . . allowedStatusCodes ] . join (
', '
) } `
)
}
2021-03-24 17:50:16 +01:00
if ( hadInvalidHas ) {
console . error (
` \ nValid \` has \` object shape is ${ JSON . stringify (
{
type : [ . . . allowedHasTypes ] . join ( ', ' ) ,
key : 'the key to check for' ,
value : 'undefined or a value string to match against' ,
} ,
null ,
2
) } `
)
}
2019-12-10 15:54:56 +01:00
console . error ( )
2021-04-20 04:03:46 +02:00
console . error (
` Error: Invalid ${ type } ${ numInvalidRoutes === 1 ? '' : 's' } found `
)
process . exit ( 1 )
2019-12-10 15:54:56 +01:00
}
}
2020-06-09 22:16:23 +02:00
export interface CustomRoutes {
headers : Header [ ]
2021-03-26 16:19:48 +01:00
rewrites : {
fallback : Rewrite [ ]
afterFiles : Rewrite [ ]
beforeFiles : Rewrite [ ]
}
2020-06-09 22:16:23 +02:00
redirects : Redirect [ ]
}
2020-12-04 11:14:55 +01:00
function processRoutes < T > (
routes : T ,
config : NextConfig ,
type : 'redirect' | 'rewrite' | 'header'
) : T {
2021-08-17 09:18:08 +02:00
const _routes = routes as any as Array < {
2020-12-04 11:14:55 +01:00
source : string
locale? : false
basePath? : false
destination? : string
} >
const newRoutes : typeof _routes = [ ]
const defaultLocales : Array < {
locale : string
base : string
} > = [ ]
if ( config . i18n && type === 'redirect' ) {
for ( const item of config . i18n ? . domains || [ ] ) {
defaultLocales . push ( {
locale : item.defaultLocale ,
base : ` http ${ item . http ? '' : 's' } :// ${ item . domain } ` ,
} )
}
defaultLocales . push ( {
locale : config.i18n.defaultLocale ,
base : '' ,
} )
}
for ( const r of _routes ) {
const srcBasePath =
config . basePath && r . basePath !== false ? config . basePath : ''
const isExternal = ! r . destination ? . startsWith ( '/' )
const destBasePath = srcBasePath && ! isExternal ? srcBasePath : ''
if ( config . i18n && r . locale !== false ) {
2021-07-22 20:32:12 +02:00
if ( ! isExternal ) {
defaultLocales . forEach ( ( item ) = > {
let destination
if ( r . destination ) {
destination = item . base
? ` ${ item . base } ${ destBasePath } ${ r . destination } `
: ` ${ destBasePath } ${ r . destination } `
}
2020-12-04 11:14:55 +01:00
2021-07-22 20:32:12 +02:00
newRoutes . push ( {
. . . r ,
destination ,
source : ` ${ srcBasePath } / ${ item . locale } ${ r . source } ` ,
} )
2020-12-04 11:14:55 +01:00
} )
2021-07-22 20:32:12 +02:00
}
2020-12-04 11:14:55 +01:00
r . source = ` /:nextInternalLocale( ${ config . i18n . locales
. map ( ( locale : string ) = > escapeStringRegexp ( locale ) )
2020-12-28 19:21:28 +01:00
. join ( '|' ) } ) $ {
r . source === '/' && ! config . trailingSlash ? '' : r . source
} `
2020-12-04 11:14:55 +01:00
if ( r . destination && r . destination ? . startsWith ( '/' ) ) {
2020-12-22 18:12:53 +01:00
r . destination = ` /:nextInternalLocale ${
r . destination === '/' && ! config . trailingSlash ? '' : r . destination
} `
2020-12-04 11:14:55 +01:00
}
}
2021-07-13 21:38:14 +02:00
r . source = ` ${ srcBasePath } ${
r . source === '/' && srcBasePath ? '' : r . source
} `
2020-12-04 11:14:55 +01:00
if ( r . destination ) {
2021-07-13 21:38:14 +02:00
r . destination = ` ${ destBasePath } ${
r . destination === '/' && destBasePath ? '' : r . destination
} `
2020-12-04 11:14:55 +01:00
}
newRoutes . push ( r )
}
2021-08-17 09:18:08 +02:00
return newRoutes as any as T
2020-12-04 11:14:55 +01:00
}
async function loadRedirects ( config : NextConfig ) {
2020-06-27 11:18:18 +02:00
if ( typeof config . redirects !== 'function' ) {
2020-06-09 22:16:23 +02:00
return [ ]
}
2020-12-04 11:14:55 +01:00
let redirects = await config . redirects ( )
2021-08-03 00:34:44 +02:00
// check before we process the routes and after to ensure
// they are still valid
checkCustomRoutes ( redirects , 'redirect' )
redirects = processRoutes ( redirects , config , 'redirect' )
2020-12-04 11:14:55 +01:00
checkCustomRoutes ( redirects , 'redirect' )
2021-08-03 00:34:44 +02:00
return redirects
2020-06-09 22:16:23 +02:00
}
2020-12-04 11:14:55 +01:00
async function loadRewrites ( config : NextConfig ) {
2020-06-27 11:18:18 +02:00
if ( typeof config . rewrites !== 'function' ) {
2021-03-26 16:19:48 +01:00
return {
beforeFiles : [ ] ,
afterFiles : [ ] ,
fallback : [ ] ,
}
}
const _rewrites = await config . rewrites ( )
let beforeFiles : Rewrite [ ] = [ ]
let afterFiles : Rewrite [ ] = [ ]
let fallback : Rewrite [ ] = [ ]
if (
! Array . isArray ( _rewrites ) &&
typeof _rewrites === 'object' &&
Object . keys ( _rewrites ) . every (
( key ) = >
key === 'beforeFiles' || key === 'afterFiles' || key === 'fallback'
)
) {
beforeFiles = _rewrites . beforeFiles || [ ]
afterFiles = _rewrites . afterFiles || [ ]
fallback = _rewrites . fallback || [ ]
} else {
afterFiles = _rewrites as any
}
2021-08-03 00:34:44 +02:00
// check before we process the routes and after to ensure
// they are still valid
checkCustomRoutes ( beforeFiles , 'rewrite' )
checkCustomRoutes ( afterFiles , 'rewrite' )
checkCustomRoutes ( fallback , 'rewrite' )
beforeFiles = processRoutes ( beforeFiles , config , 'rewrite' )
afterFiles = processRoutes ( afterFiles , config , 'rewrite' )
fallback = processRoutes ( fallback , config , 'rewrite' )
2021-03-26 16:19:48 +01:00
checkCustomRoutes ( beforeFiles , 'rewrite' )
checkCustomRoutes ( afterFiles , 'rewrite' )
checkCustomRoutes ( fallback , 'rewrite' )
return {
2021-08-03 00:34:44 +02:00
beforeFiles ,
afterFiles ,
fallback ,
2020-06-09 22:16:23 +02:00
}
}
2020-12-04 11:14:55 +01:00
async function loadHeaders ( config : NextConfig ) {
2020-06-27 11:18:18 +02:00
if ( typeof config . headers !== 'function' ) {
2020-06-09 22:16:23 +02:00
return [ ]
}
2020-12-04 11:14:55 +01:00
let headers = await config . headers ( )
2021-08-03 00:34:44 +02:00
// check before we process the routes and after to ensure
// they are still valid
checkCustomRoutes ( headers , 'header' )
headers = processRoutes ( headers , config , 'header' )
2020-12-04 11:14:55 +01:00
checkCustomRoutes ( headers , 'header' )
2021-08-03 00:34:44 +02:00
return headers
2020-06-09 22:16:23 +02:00
}
export default async function loadCustomRoutes (
2020-12-04 11:14:55 +01:00
config : NextConfig
2020-06-09 22:16:23 +02:00
) : Promise < CustomRoutes > {
const [ headers , rewrites , redirects ] = await Promise . all ( [
loadHeaders ( config ) ,
loadRewrites ( config ) ,
loadRedirects ( config ) ,
] )
2021-07-16 16:59:54 +02:00
const totalRewrites =
rewrites . beforeFiles . length +
rewrites . afterFiles . length +
rewrites . fallback . length
const totalRoutes = headers . length + redirects . length + totalRewrites
if ( totalRoutes > 1000 ) {
console . warn (
chalk . bold . yellow ( ` Warning: ` ) +
` total number of custom routes exceeds 1000, this can reduce performance. Route counts: \ n ` +
` headers: ${ headers . length } \ n ` +
` rewrites: ${ totalRewrites } \ n ` +
` redirects: ${ redirects . length } \ n ` +
` See more info: https://nextjs.org/docs/messages/max-custom-routes-reached `
)
}
2020-07-20 18:16:59 +02:00
if ( config . trailingSlash ) {
2020-07-13 16:59:40 +02:00
redirects . unshift (
{
2020-11-20 22:17:58 +01:00
source : '/:file((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/]+\\.\\w+)/' ,
2020-07-20 20:24:43 +02:00
destination : '/:file' ,
2020-07-13 16:59:40 +02:00
permanent : true ,
2020-11-14 04:35:42 +01:00
locale : config.i18n ? false : undefined ,
2020-12-07 18:36:46 +01:00
internal : true ,
2020-12-04 11:14:55 +01:00
} as Redirect ,
2020-07-13 16:59:40 +02:00
{
2020-11-20 22:17:58 +01:00
source : '/:notfile((?!\\.well-known(?:/.*)?)(?:[^/]+/)*[^/\\.]+)' ,
2020-07-20 20:24:43 +02:00
destination : '/:notfile/' ,
2020-07-13 16:59:40 +02:00
permanent : true ,
2020-11-14 04:35:42 +01:00
locale : config.i18n ? false : undefined ,
2020-12-07 18:36:46 +01:00
internal : true ,
2020-12-04 11:14:55 +01:00
} as Redirect
2020-07-13 16:59:40 +02:00
)
if ( config . basePath ) {
redirects . unshift ( {
source : config.basePath ,
destination : config.basePath + '/' ,
permanent : true ,
basePath : false ,
2020-11-14 04:35:42 +01:00
locale : config.i18n ? false : undefined ,
2020-12-07 18:36:46 +01:00
internal : true ,
2020-12-04 11:14:55 +01:00
} as Redirect )
2020-07-13 16:59:40 +02:00
}
} else {
redirects . unshift ( {
source : '/:path+/' ,
destination : '/:path+' ,
permanent : true ,
2020-11-14 04:35:42 +01:00
locale : config.i18n ? false : undefined ,
2020-12-07 18:36:46 +01:00
internal : true ,
2020-12-04 11:14:55 +01:00
} as Redirect )
2020-07-13 16:59:40 +02:00
if ( config . basePath ) {
redirects . unshift ( {
source : config.basePath + '/' ,
destination : config.basePath ,
permanent : true ,
basePath : false ,
2020-11-14 04:35:42 +01:00
locale : config.i18n ? false : undefined ,
2020-12-07 18:36:46 +01:00
internal : true ,
2020-12-04 11:14:55 +01:00
} as Redirect )
2020-07-13 16:59:40 +02:00
}
}
2020-06-23 13:38:49 +02:00
2020-06-09 22:16:23 +02:00
return {
headers ,
rewrites ,
redirects ,
}
}