2022-01-20 22:25:44 +01:00
|
|
|
import type { IncomingMessage, ServerResponse } from 'http'
|
|
|
|
import type { Rewrite } from '../../../../lib/load-custom-routes'
|
|
|
|
import type { BuildManifest } from '../../../../server/get-page-files'
|
|
|
|
import type { NextConfig } from '../../../../server/config'
|
|
|
|
import type {
|
|
|
|
GetServerSideProps,
|
|
|
|
GetStaticPaths,
|
|
|
|
GetStaticProps,
|
|
|
|
} from '../../../../types'
|
2022-02-11 20:56:25 +01:00
|
|
|
import type { BaseNextRequest } from '../../../../server/base-http'
|
2022-01-20 22:25:44 +01:00
|
|
|
|
2020-12-16 21:46:55 +01:00
|
|
|
import { format as formatUrl, UrlWithParsedQuery, parse as parseUrl } from 'url'
|
|
|
|
import { parse as parseQs, ParsedUrlQuery } from 'querystring'
|
2021-06-30 11:43:31 +02:00
|
|
|
import { normalizeLocalePath } from '../../../../shared/lib/i18n/normalize-locale-path'
|
|
|
|
import pathMatch from '../../../../shared/lib/router/utils/path-match'
|
|
|
|
import { getRouteRegex } from '../../../../shared/lib/router/utils/route-regex'
|
|
|
|
import { getRouteMatcher } from '../../../../shared/lib/router/utils/route-matcher'
|
2021-11-09 02:28:39 +01:00
|
|
|
import {
|
2021-03-24 17:50:16 +01:00
|
|
|
matchHas,
|
2021-11-09 02:28:39 +01:00
|
|
|
prepareDestination,
|
2021-06-30 11:43:31 +02:00
|
|
|
} from '../../../../shared/lib/router/utils/prepare-destination'
|
2021-06-30 13:44:40 +02:00
|
|
|
import { __ApiPreviewProps } from '../../../../server/api-utils'
|
2021-10-20 19:52:11 +02:00
|
|
|
import { acceptLanguage } from '../../../../server/accept-header'
|
2021-06-30 11:43:31 +02:00
|
|
|
import { detectLocaleCookie } from '../../../../shared/lib/i18n/detect-locale-cookie'
|
|
|
|
import { detectDomainLocale } from '../../../../shared/lib/i18n/detect-domain-locale'
|
2021-06-30 13:44:40 +02:00
|
|
|
import { denormalizePagePath } from '../../../../server/denormalize-page-path'
|
2020-11-25 20:56:18 +01:00
|
|
|
import cookie from 'next/dist/compiled/cookie'
|
2021-06-30 11:43:31 +02:00
|
|
|
import { TEMPORARY_REDIRECT_STATUS } from '../../../../shared/lib/constants'
|
2021-11-09 02:28:39 +01:00
|
|
|
import { addRequestMeta } from '../../../../server/request-meta'
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
const getCustomRouteMatcher = pathMatch(true)
|
|
|
|
|
|
|
|
export const vercelHeader = 'x-vercel-id'
|
|
|
|
|
|
|
|
export type ServerlessHandlerCtx = {
|
|
|
|
page: string
|
|
|
|
|
|
|
|
pageModule: any
|
|
|
|
pageComponent?: any
|
|
|
|
pageConfig?: any
|
|
|
|
pageGetStaticProps?: GetStaticProps
|
|
|
|
pageGetStaticPaths?: GetStaticPaths
|
|
|
|
pageGetServerSideProps?: GetServerSideProps
|
|
|
|
|
|
|
|
appModule?: any
|
|
|
|
errorModule?: any
|
|
|
|
documentModule?: any
|
|
|
|
notFoundModule?: any
|
|
|
|
|
|
|
|
runtimeConfig: any
|
|
|
|
buildManifest?: BuildManifest
|
|
|
|
reactLoadableManifest?: any
|
|
|
|
basePath: string
|
|
|
|
rewrites: Rewrite[]
|
|
|
|
pageIsDynamic: boolean
|
|
|
|
generateEtags: boolean
|
|
|
|
distDir: string
|
|
|
|
buildId: string
|
|
|
|
escapedBuildId: string
|
|
|
|
assetPrefix: string
|
|
|
|
poweredByHeader: boolean
|
|
|
|
canonicalBase: string
|
|
|
|
encodedPreviewProps: __ApiPreviewProps
|
2020-12-16 21:46:55 +01:00
|
|
|
i18n?: NextConfig['i18n']
|
2020-11-25 20:56:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function getUtils({
|
|
|
|
page,
|
|
|
|
i18n,
|
|
|
|
basePath,
|
|
|
|
rewrites,
|
|
|
|
pageIsDynamic,
|
2020-12-16 21:46:55 +01:00
|
|
|
}: {
|
|
|
|
page: ServerlessHandlerCtx['page']
|
|
|
|
i18n?: ServerlessHandlerCtx['i18n']
|
|
|
|
basePath: ServerlessHandlerCtx['basePath']
|
|
|
|
rewrites: ServerlessHandlerCtx['rewrites']
|
|
|
|
pageIsDynamic: ServerlessHandlerCtx['pageIsDynamic']
|
|
|
|
}) {
|
2020-11-25 20:56:18 +01:00
|
|
|
let defaultRouteRegex: ReturnType<typeof getRouteRegex> | undefined
|
|
|
|
let dynamicRouteMatcher: ReturnType<typeof getRouteMatcher> | undefined
|
|
|
|
let defaultRouteMatches: ParsedUrlQuery | undefined
|
|
|
|
|
|
|
|
if (pageIsDynamic) {
|
|
|
|
defaultRouteRegex = getRouteRegex(page)
|
|
|
|
dynamicRouteMatcher = getRouteMatcher(defaultRouteRegex)
|
|
|
|
defaultRouteMatches = dynamicRouteMatcher(page) as ParsedUrlQuery
|
|
|
|
}
|
|
|
|
|
2022-01-14 22:01:35 +01:00
|
|
|
function handleRewrites(
|
|
|
|
req: BaseNextRequest | IncomingMessage,
|
|
|
|
parsedUrl: UrlWithParsedQuery
|
|
|
|
) {
|
2022-04-22 10:00:33 +02:00
|
|
|
const rewriteParams = {}
|
|
|
|
|
2020-11-25 20:56:18 +01:00
|
|
|
for (const rewrite of rewrites) {
|
|
|
|
const matcher = getCustomRouteMatcher(rewrite.source)
|
2021-03-24 17:50:16 +01:00
|
|
|
let params = matcher(parsedUrl.pathname)
|
|
|
|
|
|
|
|
if (rewrite.has && params) {
|
|
|
|
const hasParams = matchHas(req, rewrite.has, parsedUrl.query)
|
|
|
|
|
|
|
|
if (hasParams) {
|
|
|
|
Object.assign(params, hasParams)
|
|
|
|
} else {
|
|
|
|
params = false
|
|
|
|
}
|
|
|
|
}
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
if (params) {
|
2022-04-22 10:00:33 +02:00
|
|
|
const { parsedDestination, destQuery } = prepareDestination({
|
2021-11-09 02:28:39 +01:00
|
|
|
appendParamsToQuery: true,
|
|
|
|
destination: rewrite.destination,
|
|
|
|
params: params,
|
|
|
|
query: parsedUrl.query,
|
|
|
|
})
|
2020-11-25 20:56:18 +01:00
|
|
|
|
2022-04-22 10:00:33 +02:00
|
|
|
Object.assign(rewriteParams, destQuery, params)
|
2020-11-25 20:56:18 +01:00
|
|
|
Object.assign(parsedUrl.query, parsedDestination.query)
|
|
|
|
delete (parsedDestination as any).query
|
|
|
|
|
|
|
|
Object.assign(parsedUrl, parsedDestination)
|
|
|
|
|
|
|
|
let fsPathname = parsedUrl.pathname
|
|
|
|
|
|
|
|
if (basePath) {
|
|
|
|
fsPathname =
|
|
|
|
fsPathname!.replace(new RegExp(`^${basePath}`), '') || '/'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i18n) {
|
|
|
|
const destLocalePathResult = normalizeLocalePath(
|
|
|
|
fsPathname!,
|
|
|
|
i18n.locales
|
|
|
|
)
|
|
|
|
fsPathname = destLocalePathResult.pathname
|
|
|
|
parsedUrl.query.nextInternalLocale =
|
|
|
|
destLocalePathResult.detectedLocale || params.nextInternalLocale
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fsPathname === page) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pageIsDynamic && dynamicRouteMatcher) {
|
|
|
|
const dynamicParams = dynamicRouteMatcher(fsPathname)
|
|
|
|
if (dynamicParams) {
|
|
|
|
parsedUrl.query = {
|
|
|
|
...parsedUrl.query,
|
|
|
|
...dynamicParams,
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-22 10:00:33 +02:00
|
|
|
return rewriteParams
|
2020-11-25 20:56:18 +01:00
|
|
|
}
|
|
|
|
|
2022-01-14 22:01:35 +01:00
|
|
|
function handleBasePath(
|
|
|
|
req: BaseNextRequest | IncomingMessage,
|
|
|
|
parsedUrl: UrlWithParsedQuery
|
|
|
|
) {
|
2020-11-25 20:56:18 +01:00
|
|
|
// always strip the basePath if configured since it is required
|
|
|
|
req.url = req.url!.replace(new RegExp(`^${basePath}`), '') || '/'
|
|
|
|
parsedUrl.pathname =
|
|
|
|
parsedUrl.pathname!.replace(new RegExp(`^${basePath}`), '') || '/'
|
|
|
|
}
|
|
|
|
|
2020-12-16 21:46:55 +01:00
|
|
|
function getParamsFromRouteMatches(
|
2022-01-14 22:01:35 +01:00
|
|
|
req: BaseNextRequest | IncomingMessage,
|
2020-12-16 21:46:55 +01:00
|
|
|
renderOpts?: any,
|
|
|
|
detectedLocale?: string
|
|
|
|
) {
|
|
|
|
return getRouteMatcher(
|
|
|
|
(function () {
|
|
|
|
const { groups, routeKeys } = defaultRouteRegex!
|
|
|
|
|
|
|
|
return {
|
|
|
|
re: {
|
|
|
|
// Simulate a RegExp match from the \`req.url\` input
|
|
|
|
exec: (str: string) => {
|
|
|
|
const obj = parseQs(str)
|
2022-02-02 20:02:03 +01:00
|
|
|
const matchesHasLocale =
|
|
|
|
i18n && detectedLocale && obj['1'] === detectedLocale
|
2020-12-16 21:46:55 +01:00
|
|
|
|
|
|
|
// favor named matches if available
|
|
|
|
const routeKeyNames = Object.keys(routeKeys || {})
|
|
|
|
const filterLocaleItem = (val: string | string[]) => {
|
|
|
|
if (i18n) {
|
|
|
|
// locale items can be included in route-matches
|
|
|
|
// for fallback SSG pages so ensure they are
|
|
|
|
// filtered
|
|
|
|
const isCatchAll = Array.isArray(val)
|
|
|
|
const _val = isCatchAll ? val[0] : val
|
|
|
|
|
|
|
|
if (
|
|
|
|
typeof _val === 'string' &&
|
|
|
|
i18n.locales.some((item) => {
|
|
|
|
if (item.toLowerCase() === _val.toLowerCase()) {
|
|
|
|
detectedLocale = item
|
|
|
|
renderOpts.locale = detectedLocale
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
) {
|
|
|
|
// remove the locale item from the match
|
|
|
|
if (isCatchAll) {
|
|
|
|
;(val as string[]).splice(0, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// the value is only a locale item and
|
|
|
|
// shouldn't be added
|
|
|
|
return isCatchAll ? val.length === 0 : true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (routeKeyNames.every((name) => obj[name])) {
|
|
|
|
return routeKeyNames.reduce((prev, keyName) => {
|
|
|
|
const paramName = routeKeys?.[keyName]
|
|
|
|
|
|
|
|
if (paramName && !filterLocaleItem(obj[keyName])) {
|
|
|
|
prev[groups[paramName].pos] = obj[keyName]
|
|
|
|
}
|
|
|
|
return prev
|
|
|
|
}, {} as any)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Object.keys(obj).reduce((prev, key) => {
|
|
|
|
if (!filterLocaleItem(obj[key])) {
|
2022-02-02 20:02:03 +01:00
|
|
|
let normalizedKey = key
|
|
|
|
|
|
|
|
if (matchesHasLocale) {
|
|
|
|
normalizedKey = parseInt(key, 10) - 1 + ''
|
|
|
|
}
|
2020-12-16 21:46:55 +01:00
|
|
|
return Object.assign(prev, {
|
2022-02-02 20:02:03 +01:00
|
|
|
[normalizedKey]: obj[key],
|
2020-12-16 21:46:55 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
return prev
|
|
|
|
}, {})
|
|
|
|
},
|
|
|
|
},
|
|
|
|
groups,
|
|
|
|
}
|
|
|
|
})() as any
|
|
|
|
)(req.headers['x-now-route-matches'] as string) as ParsedUrlQuery
|
|
|
|
}
|
|
|
|
|
|
|
|
function interpolateDynamicPath(pathname: string, params: ParsedUrlQuery) {
|
|
|
|
if (!defaultRouteRegex) return pathname
|
|
|
|
|
|
|
|
for (const param of Object.keys(defaultRouteRegex.groups)) {
|
|
|
|
const { optional, repeat } = defaultRouteRegex.groups[param]
|
|
|
|
let builtParam = `[${repeat ? '...' : ''}${param}]`
|
|
|
|
|
|
|
|
if (optional) {
|
|
|
|
builtParam = `[${builtParam}]`
|
|
|
|
}
|
|
|
|
|
|
|
|
const paramIdx = pathname!.indexOf(builtParam)
|
|
|
|
|
|
|
|
if (paramIdx > -1) {
|
|
|
|
let paramValue: string
|
|
|
|
|
|
|
|
if (Array.isArray(params[param])) {
|
2021-07-06 23:28:43 +02:00
|
|
|
paramValue = (params[param] as string[])
|
|
|
|
.map((v) => v && encodeURIComponent(v))
|
|
|
|
.join('/')
|
2020-12-16 21:46:55 +01:00
|
|
|
} else {
|
2021-07-06 23:28:43 +02:00
|
|
|
paramValue =
|
|
|
|
params[param] && encodeURIComponent(params[param] as string)
|
2020-12-16 21:46:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pathname =
|
2022-03-24 22:49:38 +01:00
|
|
|
pathname.slice(0, paramIdx) +
|
2021-07-06 23:28:43 +02:00
|
|
|
(paramValue || '') +
|
2022-03-24 22:49:38 +01:00
|
|
|
pathname.slice(paramIdx + builtParam.length)
|
2020-12-16 21:46:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pathname
|
|
|
|
}
|
|
|
|
|
2022-01-14 22:01:35 +01:00
|
|
|
function normalizeVercelUrl(
|
|
|
|
req: BaseNextRequest | IncomingMessage,
|
2022-04-22 10:00:33 +02:00
|
|
|
trustQuery: boolean,
|
|
|
|
paramKeys?: string[]
|
2022-01-14 22:01:35 +01:00
|
|
|
) {
|
2020-12-16 21:46:55 +01:00
|
|
|
// make sure to normalize req.url on Vercel to strip dynamic params
|
|
|
|
// from the query which are added during routing
|
|
|
|
if (pageIsDynamic && trustQuery && defaultRouteRegex) {
|
|
|
|
const _parsedUrl = parseUrl(req.url!, true)
|
|
|
|
delete (_parsedUrl as any).search
|
|
|
|
|
2022-04-22 10:00:33 +02:00
|
|
|
for (const param of paramKeys || Object.keys(defaultRouteRegex.groups)) {
|
2020-12-16 21:46:55 +01:00
|
|
|
delete _parsedUrl.query[param]
|
|
|
|
}
|
|
|
|
req.url = formatUrl(_parsedUrl)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-25 20:56:18 +01:00
|
|
|
function normalizeDynamicRouteParams(params: ParsedUrlQuery) {
|
|
|
|
let hasValidParams = true
|
2021-05-18 21:24:22 +02:00
|
|
|
if (!defaultRouteRegex) return { params, hasValidParams: false }
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
params = Object.keys(defaultRouteRegex.groups).reduce((prev, key) => {
|
|
|
|
let value: string | string[] | undefined = params[key]
|
|
|
|
|
|
|
|
// if the value matches the default value we can't rely
|
|
|
|
// on the parsed params, this is used to signal if we need
|
|
|
|
// to parse x-now-route-matches or not
|
2021-05-18 21:24:22 +02:00
|
|
|
const defaultValue = defaultRouteMatches![key]
|
2021-05-13 13:40:25 +02:00
|
|
|
|
2021-05-18 21:24:22 +02:00
|
|
|
const isDefaultValue = Array.isArray(defaultValue)
|
|
|
|
? defaultValue.some((defaultVal) => {
|
|
|
|
return Array.isArray(value)
|
|
|
|
? value.some((val) => val.includes(defaultVal))
|
|
|
|
: value?.includes(defaultVal)
|
2021-05-13 13:40:25 +02:00
|
|
|
})
|
2021-05-18 21:24:22 +02:00
|
|
|
: value?.includes(defaultValue as string)
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
if (isDefaultValue || typeof value === 'undefined') {
|
|
|
|
hasValidParams = false
|
|
|
|
}
|
|
|
|
|
|
|
|
// non-provided optional values should be undefined so normalize
|
|
|
|
// them to undefined
|
|
|
|
if (
|
|
|
|
defaultRouteRegex!.groups[key].optional &&
|
|
|
|
(!value ||
|
|
|
|
(Array.isArray(value) &&
|
|
|
|
value.length === 1 &&
|
|
|
|
// fallback optional catch-all SSG pages have
|
|
|
|
// [[...paramName]] for the root path on Vercel
|
|
|
|
(value[0] === 'index' || value[0] === `[[...${key}]]`)))
|
|
|
|
) {
|
|
|
|
value = undefined
|
|
|
|
delete params[key]
|
|
|
|
}
|
|
|
|
|
|
|
|
// query values from the proxy aren't already split into arrays
|
|
|
|
// so make sure to normalize catch-all values
|
|
|
|
if (
|
|
|
|
value &&
|
|
|
|
typeof value === 'string' &&
|
|
|
|
defaultRouteRegex!.groups[key].repeat
|
|
|
|
) {
|
|
|
|
value = value.split('/')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (value) {
|
|
|
|
prev[key] = value
|
|
|
|
}
|
|
|
|
return prev
|
|
|
|
}, {} as ParsedUrlQuery)
|
|
|
|
|
|
|
|
return {
|
|
|
|
params,
|
|
|
|
hasValidParams,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleLocale(
|
|
|
|
req: IncomingMessage,
|
|
|
|
res: ServerResponse,
|
|
|
|
parsedUrl: UrlWithParsedQuery,
|
|
|
|
routeNoAssetPath: string,
|
|
|
|
shouldNotRedirect: boolean
|
|
|
|
) {
|
|
|
|
if (!i18n) return
|
2021-02-25 19:43:51 +01:00
|
|
|
const pathname = parsedUrl.pathname || '/'
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
let defaultLocale = i18n.defaultLocale
|
|
|
|
let detectedLocale = detectLocaleCookie(req, i18n.locales)
|
2021-06-22 17:05:54 +02:00
|
|
|
let acceptPreferredLocale
|
|
|
|
try {
|
|
|
|
acceptPreferredLocale =
|
|
|
|
i18n.localeDetection !== false
|
2021-10-20 19:52:11 +02:00
|
|
|
? acceptLanguage(req.headers['accept-language'], i18n.locales)
|
2021-06-22 17:05:54 +02:00
|
|
|
: detectedLocale
|
|
|
|
} catch (_) {
|
|
|
|
acceptPreferredLocale = detectedLocale
|
|
|
|
}
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
const { host } = req.headers || {}
|
|
|
|
// remove port from host and remove port if present
|
|
|
|
const hostname = host && host.split(':')[0].toLowerCase()
|
|
|
|
|
|
|
|
const detectedDomain = detectDomainLocale(i18n.domains, hostname)
|
|
|
|
if (detectedDomain) {
|
|
|
|
defaultLocale = detectedDomain.defaultLocale
|
|
|
|
detectedLocale = defaultLocale
|
2022-01-14 22:01:35 +01:00
|
|
|
addRequestMeta(req as any, '__nextIsLocaleDomain', true)
|
2020-11-25 20:56:18 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if not domain specific locale use accept-language preferred
|
|
|
|
detectedLocale = detectedLocale || acceptPreferredLocale
|
|
|
|
|
|
|
|
let localeDomainRedirect
|
2021-02-25 19:43:51 +01:00
|
|
|
const localePathResult = normalizeLocalePath(pathname, i18n.locales)
|
2020-11-25 20:56:18 +01:00
|
|
|
|
2021-08-17 09:18:08 +02:00
|
|
|
routeNoAssetPath = normalizeLocalePath(
|
|
|
|
routeNoAssetPath,
|
|
|
|
i18n.locales
|
|
|
|
).pathname
|
2020-11-25 20:56:18 +01:00
|
|
|
|
|
|
|
if (localePathResult.detectedLocale) {
|
|
|
|
detectedLocale = localePathResult.detectedLocale
|
|
|
|
req.url = formatUrl({
|
|
|
|
...parsedUrl,
|
|
|
|
pathname: localePathResult.pathname,
|
|
|
|
})
|
2022-01-14 22:01:35 +01:00
|
|
|
addRequestMeta(req as any, '__nextStrippedLocale', true)
|
2020-11-25 20:56:18 +01:00
|
|
|
parsedUrl.pathname = localePathResult.pathname
|
|
|
|
}
|
|
|
|
|
|
|
|
// If a detected locale is a domain specific locale and we aren't already
|
|
|
|
// on that domain and path prefix redirect to it to prevent duplicate
|
|
|
|
// content from multiple domains
|
|
|
|
if (detectedDomain) {
|
|
|
|
const localeToCheck = localePathResult.detectedLocale
|
|
|
|
? detectedLocale
|
|
|
|
: acceptPreferredLocale
|
|
|
|
|
|
|
|
const matchedDomain = detectDomainLocale(
|
|
|
|
i18n.domains,
|
|
|
|
undefined,
|
|
|
|
localeToCheck
|
|
|
|
)
|
|
|
|
|
|
|
|
if (matchedDomain && matchedDomain.domain !== detectedDomain.domain) {
|
|
|
|
localeDomainRedirect = `http${matchedDomain.http ? '' : 's'}://${
|
|
|
|
matchedDomain.domain
|
|
|
|
}/${localeToCheck === matchedDomain.defaultLocale ? '' : localeToCheck}`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 19:43:51 +01:00
|
|
|
const denormalizedPagePath = denormalizePagePath(pathname)
|
2020-11-25 20:56:18 +01:00
|
|
|
const detectedDefaultLocale =
|
|
|
|
!detectedLocale ||
|
|
|
|
detectedLocale.toLowerCase() === defaultLocale.toLowerCase()
|
|
|
|
const shouldStripDefaultLocale = false
|
|
|
|
// detectedDefaultLocale &&
|
|
|
|
// denormalizedPagePath.toLowerCase() === \`/\${i18n.defaultLocale.toLowerCase()}\`
|
|
|
|
|
|
|
|
const shouldAddLocalePrefix =
|
|
|
|
!detectedDefaultLocale && denormalizedPagePath === '/'
|
|
|
|
|
|
|
|
detectedLocale = detectedLocale || i18n.defaultLocale
|
|
|
|
|
|
|
|
if (
|
|
|
|
!shouldNotRedirect &&
|
|
|
|
!req.headers[vercelHeader] &&
|
|
|
|
i18n.localeDetection !== false &&
|
|
|
|
(localeDomainRedirect ||
|
|
|
|
shouldAddLocalePrefix ||
|
|
|
|
shouldStripDefaultLocale)
|
|
|
|
) {
|
|
|
|
// set the NEXT_LOCALE cookie when a user visits the default locale
|
|
|
|
// with the locale prefix so that they aren't redirected back to
|
|
|
|
// their accept-language preferred locale
|
|
|
|
if (shouldStripDefaultLocale && acceptPreferredLocale !== defaultLocale) {
|
|
|
|
const previous = res.getHeader('set-cookie')
|
|
|
|
|
|
|
|
res.setHeader('set-cookie', [
|
|
|
|
...(typeof previous === 'string'
|
|
|
|
? [previous]
|
|
|
|
: Array.isArray(previous)
|
|
|
|
? previous
|
|
|
|
: []),
|
|
|
|
cookie.serialize('NEXT_LOCALE', defaultLocale, {
|
|
|
|
httpOnly: true,
|
|
|
|
path: '/',
|
|
|
|
}),
|
|
|
|
])
|
|
|
|
}
|
|
|
|
|
|
|
|
res.setHeader(
|
|
|
|
'Location',
|
|
|
|
formatUrl({
|
|
|
|
// make sure to include any query values when redirecting
|
|
|
|
...parsedUrl,
|
|
|
|
pathname: localeDomainRedirect
|
|
|
|
? localeDomainRedirect
|
|
|
|
: shouldStripDefaultLocale
|
|
|
|
? basePath || '/'
|
|
|
|
: `${basePath}/${detectedLocale}`,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
res.statusCode = TEMPORARY_REDIRECT_STATUS
|
|
|
|
res.end()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
detectedLocale =
|
|
|
|
localePathResult.detectedLocale ||
|
|
|
|
(detectedDomain && detectedDomain.defaultLocale) ||
|
|
|
|
defaultLocale
|
|
|
|
|
|
|
|
return {
|
|
|
|
defaultLocale,
|
|
|
|
detectedLocale,
|
|
|
|
routeNoAssetPath,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
handleLocale,
|
|
|
|
handleRewrites,
|
|
|
|
handleBasePath,
|
|
|
|
defaultRouteRegex,
|
2020-12-16 21:46:55 +01:00
|
|
|
normalizeVercelUrl,
|
2020-11-25 20:56:18 +01:00
|
|
|
dynamicRouteMatcher,
|
|
|
|
defaultRouteMatches,
|
2020-12-16 21:46:55 +01:00
|
|
|
interpolateDynamicPath,
|
|
|
|
getParamsFromRouteMatches,
|
2020-11-25 20:56:18 +01:00
|
|
|
normalizeDynamicRouteParams,
|
|
|
|
}
|
|
|
|
}
|