From: Eduardo San Martin Morote Date: Fri, 13 Mar 2020 15:15:47 +0000 (+0100) Subject: feat(matcher): link aliases to their original record X-Git-Tag: v4.0.0-alpha.2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e9eb6481e21de61080a96f66fbd8640157d0fd27;p=thirdparty%2Fvuejs%2Frouter.git feat(matcher): link aliases to their original record --- diff --git a/__tests__/matcher/resolve.spec.ts b/__tests__/matcher/resolve.spec.ts index 70be3614..51fb50a5 100644 --- a/__tests__/matcher/resolve.spec.ts +++ b/__tests__/matcher/resolve.spec.ts @@ -177,12 +177,324 @@ describe('Router Matcher', () => { components, aliasOf: expect.objectContaining({ path: '/parent' }), }, - { path: '/p/one', name: 'nested', components }, + { + path: '/p/one', + name: 'nested', + components, + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }, ], } ) }) + describe('nested aliases', () => { + const children = [ + { + path: 'one', + component, + name: 'nested', + alias: 'o', + children: [ + { path: 'two', alias: 't', name: 'nestednested', component }, + ], + }, + { + path: 'other', + alias: 'otherAlias', + component, + name: 'other', + }, + ] + const record = { + path: '/parent', + name: 'parent', + alias: '/p', + component, + children, + } + + it('resolves the parent as an alias', () => { + assertRecordMatch( + record, + { path: '/p' }, + expect.objectContaining({ + path: '/p', + name: 'parent', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + ], + }) + ) + }) + + describe('multiple children', () => { + // tests concerning the /parent/other path and its aliases + + it('resolves the alias parent', () => { + assertRecordMatch( + record, + { path: '/p/other' }, + expect.objectContaining({ + path: '/p/other', + name: 'other', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/other', + aliasOf: expect.objectContaining({ path: '/parent/other' }), + }), + ], + }) + ) + }) + + it('resolves the alias child', () => { + assertRecordMatch( + record, + { path: '/parent/otherAlias' }, + expect.objectContaining({ + path: '/parent/otherAlias', + name: 'other', + matched: [ + expect.objectContaining({ + path: '/parent', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/otherAlias', + aliasOf: expect.objectContaining({ path: '/parent/other' }), + }), + ], + }) + ) + }) + + it('resolves the alias parent and child', () => { + assertRecordMatch( + record, + { path: '/p/otherAlias' }, + expect.objectContaining({ + path: '/p/otherAlias', + name: 'other', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/otherAlias', + aliasOf: expect.objectContaining({ path: '/parent/other' }), + }), + ], + }) + ) + }) + }) + + it('resolves the original one with no aliases', () => { + assertRecordMatch( + record, + { path: '/parent/one/two' }, + expect.objectContaining({ + path: '/parent/one/two', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/parent', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/one', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/one/two', + aliasOf: undefined, + }), + ], + }) + ) + }) + + it('resolves when parent is an alias', () => { + assertRecordMatch( + record, + { path: '/p/one/two' }, + expect.objectContaining({ + path: '/p/one/two', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/one', + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }), + expect.objectContaining({ + path: '/p/one/two', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + + it('resolves a different child when parent is an alias', () => { + assertRecordMatch( + record, + { path: '/p/other' }, + expect.objectContaining({ + path: '/p/other', + name: 'other', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/other', + aliasOf: expect.objectContaining({ path: '/parent/other' }), + }), + ], + }) + ) + }) + + it('resolves when the first child is an alias', () => { + assertRecordMatch( + record, + { path: '/parent/o/two' }, + expect.objectContaining({ + path: '/parent/o/two', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/parent', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/o', + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }), + expect.objectContaining({ + path: '/parent/o/two', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + + it('resolves when the second child is an alias', () => { + assertRecordMatch( + record, + { path: '/parent/one/t' }, + expect.objectContaining({ + path: '/parent/one/t', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/parent', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/one', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/one/t', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + + it('resolves when the two last children are aliases', () => { + assertRecordMatch( + record, + { path: '/parent/o/t' }, + expect.objectContaining({ + path: '/parent/o/t', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/parent', + aliasOf: undefined, + }), + expect.objectContaining({ + path: '/parent/o', + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }), + expect.objectContaining({ + path: '/parent/o/t', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + + it('resolves when all are aliases', () => { + assertRecordMatch( + record, + { path: '/p/o/t' }, + expect.objectContaining({ + path: '/p/o/t', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/o', + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }), + expect.objectContaining({ + path: '/p/o/t', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + + it('resolves when first and last are aliases', () => { + assertRecordMatch( + record, + { path: '/p/one/t' }, + expect.objectContaining({ + path: '/p/one/t', + name: 'nestednested', + matched: [ + expect.objectContaining({ + path: '/p', + aliasOf: expect.objectContaining({ path: '/parent' }), + }), + expect.objectContaining({ + path: '/p/one', + aliasOf: expect.objectContaining({ path: '/parent/one' }), + }), + expect.objectContaining({ + path: '/p/one/t', + aliasOf: expect.objectContaining({ path: '/parent/one/two' }), + }), + ], + }) + ) + }) + }) + it('resolves the original path of the named children of a route with an alias', () => { const children = [{ path: 'one', component, name: 'nested' }] assertRecordMatch( diff --git a/src/matcher/index.ts b/src/matcher/index.ts index 97802d99..621eb226 100644 --- a/src/matcher/index.ts +++ b/src/matcher/index.ts @@ -47,9 +47,12 @@ export function createRouterMatcher( // TODO: add routes to children of parent function addRoute( record: Readonly, - parent?: RouteRecordMatcher + parent?: RouteRecordMatcher, + originalRecord?: RouteRecordMatcher ) { - const mainNormalizedRecord = normalizeRouteRecord(record) + let mainNormalizedRecord = normalizeRouteRecord(record) + // we might be the child of an alias + mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record const options: PathParserOptions = { ...globalOptions, ...record.options } // generate an array of records to correctly handle aliases const normalizedRecords: RouteRecordNormalized[] = [mainNormalizedRecord] @@ -60,7 +63,10 @@ export function createRouterMatcher( normalizedRecords.push({ ...mainNormalizedRecord, path: alias, - aliasOf: mainNormalizedRecord, + // we might be the child of an alias + aliasOf: originalRecord + ? originalRecord.record + : mainNormalizedRecord, }) } } @@ -83,11 +89,19 @@ export function createRouterMatcher( // create the object before hand so it can be passed to children matcher = createRouteRecordMatcher(normalizedRecord, parent, options) - if ('children' in record) { - for (const childRecord of record.children!) - addRoute(childRecord, matcher) + let children = mainNormalizedRecord.children + for (let i = 0; i < children.length; i++) { + addRoute( + children[i], + matcher, + originalRecord && originalRecord.children[i] + ) } + // if there was no original record, then the first one was not an alias and all + // other alias (if any) need to reference this record when adding children + originalRecord = originalRecord || matcher + insertMatcher(matcher) } @@ -235,8 +249,8 @@ export function normalizeRouteRecord( return { path: record.path, components, - // fallback to empty array for monomorphic objects - children: (record as any).children, + // record is an object and if it has a children property, it's an array + children: (record as any).children || [], name: record.name, beforeEnter, meta: record.meta || {}, diff --git a/src/matcher/path-matcher.ts b/src/matcher/path-matcher.ts index beb93339..0d315e28 100644 --- a/src/matcher/path-matcher.ts +++ b/src/matcher/path-matcher.ts @@ -23,9 +23,18 @@ export function createRouteRecordMatcher( ...parser, record, parent, + // these needs to be populated by the parent children: [], } - if (parent) parent.children.push(matcher) + if (parent) { + // both are aliases or both are not aliases + // we don't want to mix them because the order is used when + // passing originalRecord in Matcher.addRoute + if (!matcher.record.aliasOf === !parent.record.aliasOf) + parent.children.push(matcher) + // else TODO: save alias children to be able to remove them + } + return matcher } diff --git a/src/matcher/types.ts b/src/matcher/types.ts index 58e26039..d29c8423 100644 --- a/src/matcher/types.ts +++ b/src/matcher/types.ts @@ -5,7 +5,7 @@ export interface RouteRecordNormalized { path: RouteRecordMultipleViews['path'] name: RouteRecordMultipleViews['name'] components: RouteRecordMultipleViews['components'] - children: RouteRecordMultipleViews['children'] + children: Exclude meta: Exclude beforeEnter: RouteRecordMultipleViews['beforeEnter'] leaveGuards: NavigationGuard[] diff --git a/src/utils/index.ts b/src/utils/index.ts index 755bc85c..e401b24b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -55,11 +55,10 @@ export function isSameRouteRecord( a: Immutable, b: Immutable ): boolean { - return ( - a === b || a.aliasOf === b || b.aliasOf === a - // TODO: doesn't work if not named and parent is an alias but child is not - // || (!!a.name && a.name === b.name) - ) + // since the original record has an undefined value for aliasOf + // but all aliases point to the original record, this will always compare + // the original record + return (a.aliasOf || a) === (b.aliasOf || b) } export function isSameLocationObject(