From: Eduardo San Martin Morote Date: Thu, 18 Jun 2020 16:08:52 +0000 (+0200) Subject: feat(redirect): allow redirect on routes witch children X-Git-Tag: v4.0.0-alpha.14~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e57b875dd9d375778a847627434803f4ec79a818;p=thirdparty%2Fvuejs%2Frouter.git feat(redirect): allow redirect on routes witch children --- diff --git a/__tests__/matcher/records.spec.ts b/__tests__/matcher/records.spec.ts index a49f9019..bc78ec17 100644 --- a/__tests__/matcher/records.spec.ts +++ b/__tests__/matcher/records.spec.ts @@ -53,7 +53,7 @@ describe('normalizeRouteRecord', () => { name: 'name', }) - expect(record).toEqual({ + expect(record).toMatchObject({ aliasOf: undefined, components: {}, meta: { foo: true }, diff --git a/__tests__/router.spec.ts b/__tests__/router.spec.ts index b08edeec..9bde89d2 100644 --- a/__tests__/router.spec.ts +++ b/__tests__/router.spec.ts @@ -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', () => { diff --git a/src/matcher/index.ts b/src/matcher/index.ts index 6ae1734a..6a7a7e6b 100644 --- a/src/matcher/index.ts +++ b/src/matcher/index.ts @@ -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 { const propsObject = {} as Record - 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 { diff --git a/src/matcher/types.ts b/src/matcher/types.ts index 67b8e980..d381fcf0 100644 --- a/src/matcher/types.ts +++ b/src/matcher/types.ts @@ -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 @@ -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 - // this object will always be empty but it simplifies things - components: RouteRecordMultipleViews['components'] -} - -export type RouteRecord = RouteRecordNormalized | RouteRecordRedirect +export type RouteRecord = RouteRecordNormalized diff --git a/src/router.ts b/src/router.ts index e8a56e61..439dd40d 100644 --- a/src/router.ts +++ b/src/router.ts @@ -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( diff --git a/src/types/index.ts b/src/types/index.ts index 1f091184..b12421bb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -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 - 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 | 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: '/',