d130f63c41
This adds support for returning an object from `rewrites` in `next.config.js` with `beforeFiles`, `afterFiles`, and `fallback` to allow specifying rewrites at different stages of routing. The existing support for returning an array for rewrites is still supported and behaves the same way. The documentation has been updated to include information on these new stages that can be rewritten and removes the outdated note of rewrites not being able to override pages. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. ## Documentation / Examples - [ ] Make sure the linting passes
146 lines
3.7 KiB
TypeScript
146 lines
3.7 KiB
TypeScript
import { ParsedUrlQuery } from 'querystring'
|
|
import pathMatch from './path-match'
|
|
import prepareDestination, { matchHas } from './prepare-destination'
|
|
import { Rewrite } from '../../../../lib/load-custom-routes'
|
|
import { removePathTrailingSlash } from '../../../../client/normalize-trailing-slash'
|
|
import { normalizeLocalePath } from '../../i18n/normalize-locale-path'
|
|
import { parseRelativeUrl } from './parse-relative-url'
|
|
import { delBasePath } from '../router'
|
|
|
|
const customRouteMatcher = pathMatch(true)
|
|
|
|
export default function resolveRewrites(
|
|
asPath: string,
|
|
pages: string[],
|
|
rewrites: {
|
|
beforeFiles: Rewrite[]
|
|
afterFiles: Rewrite[]
|
|
fallback: Rewrite[]
|
|
},
|
|
query: ParsedUrlQuery,
|
|
resolveHref: (path: string) => string,
|
|
locales?: string[]
|
|
): {
|
|
matchedPage: boolean
|
|
parsedAs: ReturnType<typeof parseRelativeUrl>
|
|
asPath: string
|
|
resolvedHref?: string
|
|
} {
|
|
let matchedPage = false
|
|
let parsedAs = parseRelativeUrl(asPath)
|
|
let fsPathname = removePathTrailingSlash(
|
|
normalizeLocalePath(delBasePath(parsedAs.pathname), locales).pathname
|
|
)
|
|
let resolvedHref
|
|
|
|
const handleRewrite = (rewrite: Rewrite) => {
|
|
const matcher = customRouteMatcher(rewrite.source)
|
|
let params = matcher(parsedAs.pathname)
|
|
|
|
if (rewrite.has && params) {
|
|
const hasParams = matchHas(
|
|
{
|
|
headers: {
|
|
host: document.location.hostname,
|
|
},
|
|
cookies: Object.fromEntries(
|
|
document.cookie.split('; ').map((item) => {
|
|
const [key, ...value] = item.split('=')
|
|
return [key, value.join('=')]
|
|
})
|
|
),
|
|
} as any,
|
|
rewrite.has,
|
|
parsedAs.query
|
|
)
|
|
|
|
if (hasParams) {
|
|
Object.assign(params, hasParams)
|
|
} else {
|
|
params = false
|
|
}
|
|
}
|
|
|
|
if (params) {
|
|
if (!rewrite.destination) {
|
|
// this is a proxied rewrite which isn't handled on the client
|
|
return true
|
|
}
|
|
const destRes = prepareDestination(
|
|
rewrite.destination,
|
|
params,
|
|
query,
|
|
true
|
|
)
|
|
parsedAs = destRes.parsedDestination
|
|
asPath = destRes.newUrl
|
|
Object.assign(query, destRes.parsedDestination.query)
|
|
|
|
fsPathname = removePathTrailingSlash(
|
|
normalizeLocalePath(delBasePath(asPath), locales).pathname
|
|
)
|
|
|
|
if (pages.includes(fsPathname)) {
|
|
// check if we now match a page as this means we are done
|
|
// resolving the rewrites
|
|
matchedPage = true
|
|
resolvedHref = fsPathname
|
|
return true
|
|
}
|
|
|
|
// check if we match a dynamic-route, if so we break the rewrites chain
|
|
resolvedHref = resolveHref(fsPathname)
|
|
|
|
if (resolvedHref !== asPath && pages.includes(resolvedHref)) {
|
|
matchedPage = true
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
let finished = false
|
|
|
|
for (let i = 0; i < rewrites.beforeFiles.length; i++) {
|
|
const rewrite = rewrites.beforeFiles[i]
|
|
|
|
if (handleRewrite(rewrite)) {
|
|
finished = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if (!pages.includes(fsPathname)) {
|
|
if (!finished) {
|
|
for (let i = 0; i < rewrites.afterFiles.length; i++) {
|
|
const rewrite = rewrites.afterFiles[i]
|
|
if (handleRewrite(rewrite)) {
|
|
finished = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// check dynamic route before processing fallback rewrites
|
|
if (!finished) {
|
|
resolvedHref = resolveHref(fsPathname)
|
|
matchedPage = pages.includes(resolvedHref)
|
|
finished = matchedPage
|
|
}
|
|
|
|
if (!finished) {
|
|
for (let i = 0; i < rewrites.fallback.length; i++) {
|
|
const rewrite = rewrites.fallback[i]
|
|
if (handleRewrite(rewrite)) {
|
|
finished = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
asPath,
|
|
parsedAs,
|
|
matchedPage,
|
|
resolvedHref,
|
|
}
|
|
}
|