]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
refactor: improve matchLocation
authorEduardo San Martin Morote <posva13@gmail.com>
Tue, 28 May 2019 18:47:03 +0000 (20:47 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Tue, 28 May 2019 18:47:03 +0000 (20:47 +0200)
__tests__/router.spec.js
src/history/base.ts
src/history/utils.ts
src/router.ts
src/types/index.ts

index 6fec494d28b19f0a67b5d0b76c59c2c85abfc133..da7bb5ba7bd3d8765596a93d8e766334352d6bcb 100644 (file)
@@ -19,6 +19,14 @@ const routes = [
   { 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', () => {
@@ -77,6 +85,18 @@ 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 })
@@ -87,6 +107,23 @@ describe('Router', () => {
       })
     })
 
+    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 })
index 1c0580caa8bb66bf5cc5622cd3a3c660fc969872..7bce535619e008236e440309fa26e685414c148f 100644 (file)
@@ -2,18 +2,20 @@ import * as utils from './utils'
 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
index a0aa5892eceffa8cdd38da084da1297b0ed1672d..bd31daface1b793eb54665b8c0d9a1283ffbe4d6 100644 (file)
@@ -2,8 +2,8 @@ import {
   HistoryLocationNormalized,
   HistoryQuery,
   HistoryLocation,
+  RawHistoryQuery,
 } from './base'
-import { RouteQuery } from '../types'
 
 const PERCENT_RE = /%/g
 
@@ -86,7 +86,7 @@ export function stringifyURL(location: HistoryLocation): string {
  * 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) {
@@ -106,7 +106,7 @@ export function stringifyQuery(query: HistoryQuery): string {
   return search
 }
 
-export function normalizeQuery(query: RouteQuery): HistoryQuery {
+export function normalizeQuery(query: RawHistoryQuery): HistoryQuery {
   // TODO: implem
   return query as HistoryQuery
 }
@@ -136,7 +136,7 @@ export function normalizeLocation(
     return {
       fullPath: stringifyURL(location),
       path: location.path,
-      query: location.query || {},
+      query: location.query ? normalizeQuery(location.query) : {},
       hash: location.hash || '',
     }
 }
index 93147b91f5dad12c45d7871d94d8ca7ecdc0db23..6d7e6ad219d56e752304613a7b25b078c4588124 100644 (file)
@@ -9,7 +9,6 @@ import {
   RouteRecord,
   START_LOCATION_NORMALIZED,
   RouteLocationNormalized,
-  MatcherLocationNormalized,
   ListenerRemover,
   NavigationGuard,
   TODO,
@@ -83,7 +82,7 @@ export class Router {
     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) {
@@ -117,6 +116,10 @@ export class Router {
           )
         }
 
+        // 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,
@@ -139,8 +142,14 @@ export class Router {
       }
     } else {
       // add the redirectedFrom field
+      const url = this.history.utils.normalizeLocation({
+        path: matchedRoute.path,
+        query: location.query,
+        hash: location.hash,
+      })
       return {
         ...matchedRoute,
+        ...url,
         redirectedFrom,
       }
     }
@@ -153,9 +162,7 @@ export class Router {
    */
   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)
@@ -179,7 +186,7 @@ export class Router {
     // 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)
index 6badf683c59f4e6c2f695cc079fbd59ac43f860c..f925d2d6247b045965b0448d02d6a3d534dd62e9 100644 (file)
@@ -1,4 +1,4 @@
-import { HistoryQuery } from '../history/base'
+import { HistoryQuery, RawHistoryQuery } from '../history/base'
 
 export type Lazy<T> = () => Promise<T>
 
@@ -8,10 +8,9 @@ export type ListenerRemover = () => void
 
 // 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 {