rsnext/packages/next/next-server/lib/router/utils/resolve-rewrites.ts
Jamie fa5d41b346
fix: replace usage of fromEntries in browser bundled resolve-rewrites.ts (#25208)
## Bug

- [x] Related issues linked using `fixes #number`
- [ ] Integration tests added

Fixes #25207

Currently rewritten routes that use a `has` condition throw an `Object.fromEntries is not a function` error in older browsers.

## Documentation / Examples

- [x] Make sure the linting passes

## Solution

As mentioned in issue #25207, looks like last year the team decided not to include the `fromEntries` polyfill https://github.com/vercel/next.js/pull/15772#discussion_r463957221.

As such, we should avoid using non-polyfilled methods in core next.js code bundled in the browser.

This PR's changes should result in the same object being returned, without using `fromEntries`.
2021-05-18 20:18:57 +00:00

147 lines
3.8 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: document.cookie
.split('; ')
.reduce<Record<string, string>>((acc, item) => {
const [key, ...value] = item.split('=')
acc[key] = value.join('=')
return acc
}, {}),
} 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,
}
}