rsnext/packages/next/next-server/lib/router/utils/resolve-rewrites.ts
JJ Kasper d130f63c41
Add handling fo beforeFiles, afterFiles, and fallback rewrites (#23407)
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
2021-03-26 15:19:48 +00:00

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,
}
}