]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat(redirect): allow redirect on routes witch children
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 18 Jun 2020 16:08:52 +0000 (18:08 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Thu, 18 Jun 2020 16:13:52 +0000 (18:13 +0200)
__tests__/matcher/records.spec.ts
__tests__/router.spec.ts
src/matcher/index.ts
src/matcher/types.ts
src/router.ts
src/types/index.ts

index a49f901950dd12966c4edcf31747ad96e6fdf9c8..bc78ec17be99c4c083e1d47c441b832b4bc65a1c 100644 (file)
@@ -53,7 +53,7 @@ describe('normalizeRouteRecord', () => {
       name: 'name',
     })
 
-    expect(record).toEqual({
+    expect(record).toMatchObject({
       aliasOf: undefined,
       components: {},
       meta: { foo: true },
index b08edeece34d9c8c5db08b3c4e4c75f5cf54f314..9bde89d2cf2819b289ace9ad1518308120e5fb7b 100644 (file)
@@ -664,6 +664,30 @@ describe('Router', () => {
         path: '/inc-query-hash',
       })
     })
+
+    it('allows a redirect with children', async () => {
+      const history = createMemoryHistory()
+      const router = createRouter({
+        history,
+        routes: [
+          {
+            path: '/parent',
+            redirect: { name: 'child' },
+            component: components.Home,
+            name: 'parent',
+            children: [{ name: 'child', path: '', component: components.Home }],
+          },
+        ],
+      })
+      await expect(router.push({ name: 'parent' })).resolves.toEqual(undefined)
+      const loc = router.currentRoute.value
+      expect(loc.name).toBe('child')
+      expect(loc.path).toBe('/parent')
+      expect(loc.redirectedFrom).toMatchObject({
+        name: 'parent',
+        path: '/parent',
+      })
+    })
   })
 
   describe('base', () => {
index 6ae1734a5a35a1a22e37eb2edbc60ffbd85eca6c..6a7a7e6b17b225d6288dfa513cb34b3187b3deb5 100644 (file)
@@ -5,12 +5,10 @@ import {
   isRouteName,
   RouteRecordName,
   _RouteRecordProps,
-  RouteRecordSingleView,
-  RouteRecordMultipleViews,
 } from '../types'
 import { createRouterError, ErrorTypes, MatcherError } from '../errors'
 import { createRouteRecordMatcher, RouteRecordMatcher } from './pathMatcher'
-import { RouteRecordRedirect, RouteRecordNormalized } from './types'
+import { RouteRecordNormalized } from './types'
 import {
   PathParams,
   comparePathParserScore,
@@ -130,7 +128,6 @@ export function createRouterMatcher(
           removeRoute(record.name)
       }
 
-      // only non redirect records have children
       if ('children' in mainNormalizedRecord) {
         let children = mainNormalizedRecord.children
         for (let i = 0; i < children.length; i++) {
@@ -306,36 +303,30 @@ function paramsFromLocation(
 }
 
 /**
- * Normalizes a RouteRecordRaw. Transforms the `redirect` option into a
- * `beforeEnter`. This function creates a copy
+ * Normalizes a RouteRecordRaw. Creates a copy
+ *
  * @param record
  * @returns the normalized version
  */
 export function normalizeRouteRecord(
   record: RouteRecordRaw
-): RouteRecordNormalized | RouteRecordRedirect {
-  const commonInitialValues = {
+): RouteRecordNormalized {
+  return {
     path: record.path,
+    redirect: record.redirect,
     name: record.name,
     meta: record.meta || {},
     aliasOf: undefined,
-    components: {},
-  }
-
-  if ('redirect' in record) {
-    return assign(commonInitialValues, { redirect: record.redirect })
-  } else {
-    const components =
-      'components' in record ? record.components : { default: record.component }
-    return assign(commonInitialValues, {
-      beforeEnter: record.beforeEnter,
-      props: normalizeRecordProps(record),
-      children: record.children || [],
-      instances: {},
-      leaveGuards: [],
-      updateGuards: [],
-      components,
-    })
+    beforeEnter: record.beforeEnter,
+    props: normalizeRecordProps(record),
+    children: record.children || [],
+    instances: {},
+    leaveGuards: [],
+    updateGuards: [],
+    components:
+      'components' in record
+        ? record.components || {}
+        : { default: record.component! },
   }
 }
 
@@ -345,10 +336,11 @@ export function normalizeRouteRecord(
  * @param record
  */
 function normalizeRecordProps(
-  record: RouteRecordSingleView | RouteRecordMultipleViews
+  record: RouteRecordRaw
 ): Record<string, _RouteRecordProps> {
   const propsObject = {} as Record<string, _RouteRecordProps>
-  const props = record.props || false
+  // props does not exist on redirect records but we can set false directly
+  const props = (record as any).props || false
   if ('component' in record) {
     propsObject.default = props
   } else {
index 67b8e9806407c234d522f1e6152ee499fc403edc..d381fcf0e055d1460149c574b06a6e8c701e1424 100644 (file)
@@ -2,7 +2,6 @@ import {
   RouteRecordMultipleViews,
   NavigationGuard,
   _RouteRecordBase,
-  RouteRecordRedirectRaw,
   _RouteRecordProps,
 } from '../types'
 import { ComponentPublicInstance } from 'vue'
@@ -10,6 +9,7 @@ import { ComponentPublicInstance } from 'vue'
 // normalize component/components into components and make every property always present
 export interface RouteRecordNormalized {
   path: RouteRecordMultipleViews['path']
+  redirect: _RouteRecordBase['redirect'] | undefined
   name: RouteRecordMultipleViews['name']
   components: RouteRecordMultipleViews['components']
   children: Exclude<RouteRecordMultipleViews['children'], void>
@@ -26,15 +26,4 @@ export interface RouteRecordNormalized {
   aliasOf: RouteRecordNormalized | undefined
 }
 
-export interface RouteRecordRedirect {
-  path: RouteRecordMultipleViews['path']
-  name: RouteRecordMultipleViews['name']
-  redirect: RouteRecordRedirectRaw['redirect']
-  // can only be of of the same type as this record
-  aliasOf: RouteRecordRedirect | undefined
-  meta: Exclude<RouteRecordMultipleViews['meta'], void>
-  // this object will always be empty but it simplifies things
-  components: RouteRecordMultipleViews['components']
-}
-
-export type RouteRecord = RouteRecordNormalized | RouteRecordRedirect
+export type RouteRecord = RouteRecordNormalized
index e8a56e614852453123001c9cf8b88e123228f4f0..439dd40d6b8bffc9a0708659aef777283eefae8c 100644 (file)
@@ -363,7 +363,7 @@ export function createRouter(options: RouterOptions): Router {
 
     const lastMatched =
       targetLocation.matched[targetLocation.matched.length - 1]
-    if (lastMatched && 'redirect' in lastMatched) {
+    if (lastMatched && lastMatched.redirect) {
       const { redirect } = lastMatched
       // transform it into an object to pass the original RouteLocaleOptions
       let newTargetLocation = locationAsObject(
index 1f0911841e0e164abbaa2b02143ff082ec9f616f..b12421bb54e4aad731159c7d7bcec0983e5057a8 100644 (file)
@@ -158,6 +158,16 @@ export interface _RouteRecordBase extends PathParserOptions {
    * another record.
    */
   path: string
+  /**
+   * Where to redirect if the route is directly matched. The redirection happens
+   * before any navigation guard and triggers a new navigation with the new
+   * target location.
+   */
+  redirect?: RouteRecordRedirectOption
+  /**
+   * Array of nested routes.
+   */
+  children?: RouteRecordRaw[]
   /**
    * Aliases for the record. Allows defining extra paths that will behave like a
    * copy of the record. Allows having paths shorthands like `/users/:id` and
@@ -184,16 +194,12 @@ export interface _RouteRecordBase extends PathParserOptions {
 export type RouteRecordRedirectOption =
   | RouteLocationRaw
   | ((to: RouteLocation) => RouteLocationRaw)
-export interface RouteRecordRedirectRaw extends _RouteRecordBase {
-  redirect: RouteRecordRedirectOption
-  beforeEnter?: never
-  component?: never
-  components?: never
-}
 
 export interface RouteRecordSingleView extends _RouteRecordBase {
+  /**
+   * Component to display when the URL matches this route.
+   */
   component: RawRouteComponent
-  children?: RouteRecordRaw[]
   /**
    * Allow passing down params as props to the component rendered by `router-view`.
    */
@@ -201,8 +207,10 @@ export interface RouteRecordSingleView extends _RouteRecordBase {
 }
 
 export interface RouteRecordMultipleViews extends _RouteRecordBase {
+  /**
+   * Components to display when the URL matches this route. Allow using named views.
+   */
   components: Record<string, RawRouteComponent>
-  children?: RouteRecordRaw[]
   /**
    * Allow passing down params as props to the component rendered by
    * `router-view`. Should be an object with the same keys as `components` or a
@@ -211,10 +219,25 @@ export interface RouteRecordMultipleViews extends _RouteRecordBase {
   props?: Record<string, _RouteRecordProps> | boolean
 }
 
+export interface RouteRecordRedirect extends _RouteRecordBase {
+  /**
+   * @inheritdoc
+   */
+  redirect: RouteRecordRedirectOption
+  component?: never
+  components?: never
+  children?: never
+}
+
+export interface RouteRecordRedirectWithChildren extends _RouteRecordBase {
+  component?: never
+  children: Exclude<_RouteRecordBase['children'], undefined>
+}
+
 export type RouteRecordRaw =
   | RouteRecordSingleView
   | RouteRecordMultipleViews
-  | RouteRecordRedirectRaw
+  | RouteRecordRedirect
 
 export const START_LOCATION_NORMALIZED: RouteLocationNormalizedLoaded = {
   path: '/',