rsnext/packages/next/shared/lib/router/utils/parse-relative-url.ts
Sukka 87826ee186
fix(#33081): handle relative path correctly (#36823)
## Bug

- [x] Related issues linked using `fixes #number`
- [x] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

Fixes #36823 
Closes #33084

The issue is caused by the `isLocalURL` function only checks if a URL starts with `/`, `#` or `?`. So a URL that starts with `.` will not be considered a "local URL". The PR fixes that by introducing a new util function `isAbsoluteUrl` that is fully compliant with [RFC3986](https://tools.ietf.org/html/rfc3986#section-4.3).
2022-05-22 16:43:48 +00:00

35 lines
1.1 KiB
TypeScript

import { getLocationOrigin } from '../../utils'
import { searchParamsToUrlQuery } from './querystring'
/**
* Parses path-relative urls (e.g. `/hello/world?foo=bar`). If url isn't path-relative
* (e.g. `./hello`) then at least base must be.
* Absolute urls are rejected with one exception, in the browser, absolute urls that are on
* the current origin will be parsed as relative
*/
export function parseRelativeUrl(url: string, base?: string) {
const globalBase = new URL(
typeof window === 'undefined' ? 'http://n' : getLocationOrigin()
)
const resolvedBase = base
? new URL(base, globalBase)
: url.startsWith('.')
? new URL(typeof window === 'undefined' ? 'http://n' : window.location.href)
: globalBase
const { pathname, searchParams, search, hash, href, origin } = new URL(
url,
resolvedBase
)
if (origin !== globalBase.origin) {
throw new Error(`invariant: invalid relative URL, router received ${url}`)
}
return {
pathname,
query: searchParamsToUrlQuery(searchParams),
search,
hash,
href: href.slice(globalBase.origin.length),
}
}