{ path: '/to-foo2', redirect: '/to-foo' },
{ path: '/p/:p', component: components.Bar },
{ path: '/to-p/:p', redirect: to => `/p/${to.params.p}` },
+ {
+ path: '/inc-query-hash',
+ redirect: to => ({
+ name: 'Foo',
+ query: { n: to.query.n + '-2' },
+ hash: to.hash + '-2',
+ }),
+ },
]
describe('Router', () => {
})
})
+ it('drops query and params on redirect if not provided', async () => {
+ const history = mockHistory()
+ const router = new Router({ history, routes })
+ const loc = await router.push('/to-foo?hey=foo#fa')
+ expect(loc.name).toBe('Foo')
+ expect(loc.query).toEqual({})
+ expect(loc.hash).toBe('')
+ expect(loc.redirectedFrom).toMatchObject({
+ path: '/to-foo',
+ })
+ })
+
it('allows object in redirect', async () => {
const history = mockHistory()
const router = new Router({ history, routes })
})
})
+ it('can pass on query and hash when redirecting', async () => {
+ const history = mockHistory()
+ const router = new Router({ history, routes })
+ const loc = await router.push('/inc-query-hash?n=3#fa')
+ expect(loc).toMatchObject({
+ name: 'Foo',
+ query: {
+ n: '3-2',
+ },
+ hash: '#fa-2',
+ })
+ expect(loc.redirectedFrom).toMatchObject({
+ fullPath: '/inc-query-hash?n=2#fa',
+ path: '/inc-query-hash',
+ })
+ })
+
it('handles multiple redirect fields in route record', async () => {
const history = mockHistory()
const router = new Router({ history, routes })
import { ListenerRemover } from '../types'
export type HistoryQuery = Record<string, string | string[]>
+export type RawHistoryQuery = Record<string, string | string[] | null>
export interface HistoryLocation {
// pathname section
path: string
// search string parsed
- query?: HistoryQuery
+ query?: RawHistoryQuery
// hash with the #
hash?: string
}
export interface HistoryLocationNormalized extends Required<HistoryLocation> {
// full path (like href)
fullPath: string
+ query: HistoryQuery
}
// pushState clones the state passed and do not accept everything
HistoryLocationNormalized,
HistoryQuery,
HistoryLocation,
+ RawHistoryQuery,
} from './base'
-import { RouteQuery } from '../types'
const PERCENT_RE = /%/g
* Stringify an object query. Works like URLSearchParams. Doesn't prepend a `?`
* @param query
*/
-export function stringifyQuery(query: HistoryQuery): string {
+export function stringifyQuery(query: RawHistoryQuery): string {
let search = ''
// TODO: util function?
for (const key in query) {
return search
}
-export function normalizeQuery(query: RouteQuery): HistoryQuery {
+export function normalizeQuery(query: RawHistoryQuery): HistoryQuery {
// TODO: implem
return query as HistoryQuery
}
return {
fullPath: stringifyURL(location),
path: location.path,
- query: location.query || {},
+ query: location.query ? normalizeQuery(location.query) : {},
hash: location.hash || '',
}
}
RouteRecord,
START_LOCATION_NORMALIZED,
RouteLocationNormalized,
- MatcherLocationNormalized,
ListenerRemover,
NavigationGuard,
TODO,
currentLocation: RouteLocationNormalized,
redirectedFrom?: RouteLocationNormalized
// ensure when returning that the redirectedFrom is a normalized location
- ): MatcherLocationNormalized & { redirectedFrom?: RouteLocationNormalized } {
+ ): RouteLocationNormalized {
const matchedRoute = this.matcher.resolve(location, currentLocation)
if ('redirect' in matchedRoute) {
)
}
+ // TODO: should we allow partial redirects? I think we should because it's impredictable if
+ // there was a redirect before
+ // if (!('path' in newLocation) && !('name' in newLocation)) throw new Error('TODO: redirect canot be relative')
+
return this.matchLocation(
{
...newLocation,
}
} else {
// add the redirectedFrom field
+ const url = this.history.utils.normalizeLocation({
+ path: matchedRoute.path,
+ query: location.query,
+ hash: location.hash,
+ })
return {
...matchedRoute,
+ ...url,
redirectedFrom,
}
}
*/
async push(to: RouteLocation): Promise<RouteLocationNormalized> {
let url: HistoryLocationNormalized
- let location: MatcherLocationNormalized & {
- redirectedFrom?: RouteLocationNormalized
- }
+ let location: RouteLocationNormalized
// TODO: refactor into matchLocation to return location and url
if (typeof to === 'string' || 'path' in to) {
url = this.history.utils.normalizeLocation(to)
// TODO: needs a proper check because order could be different
if (this.currentRoute.fullPath === url.fullPath) return this.currentRoute
- const toLocation: RouteLocationNormalized = { ...url, ...location }
+ const toLocation: RouteLocationNormalized = location
// trigger all guards, throw if navigation is rejected
try {
await this.navigate(toLocation, this.currentRoute)
-import { HistoryQuery } from '../history/base'
+import { HistoryQuery, RawHistoryQuery } from '../history/base'
export type Lazy<T> = () => Promise<T>
// TODO: support numbers for easier writing but cast them
export type RouteParams = Record<string, string | string[]>
-export type RouteQuery = Record<string, string | string[] | null>
export interface RouteQueryAndHash {
- query?: RouteQuery
+ query?: RawHistoryQuery
hash?: string
}
export interface LocationAsPath {