]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: allow removing children routes in matcher
authorEduardo San Martin Morote <posva13@gmail.com>
Fri, 14 Feb 2020 16:38:40 +0000 (17:38 +0100)
committerEduardo San Martin Morote <posva13@gmail.com>
Fri, 14 Feb 2020 16:38:40 +0000 (17:38 +0100)
__tests__/matcher/addingRemoving.spec.ts
__tests__/matcher/resolve.spec.ts
src/matcher/index.ts

index 474c5b641a124c69ce24a2bf3145c80948370e15..588bbae07c5d951fbe4b405babee59e413137266 100644 (file)
@@ -14,6 +14,22 @@ describe('normalizeRouteRecord', () => {
     })
   })
 
+  it('adds children', () => {
+    const matcher = createRouterMatcher([], {})
+    matcher.addRoute({ path: '/parent', component, name: 'home' })
+    const parent = matcher.getRecordMatcher('home')
+    matcher.addRoute({ path: 'foo', component, name: 'foo' }, parent)
+    expect(
+      matcher.resolve({ path: '/parent/foo' }, currentLocation)
+    ).toMatchObject({
+      name: 'foo',
+      matched: [
+        expect.objectContaining({ name: 'home' }),
+        expect.objectContaining({ name: 'foo' }),
+      ],
+    })
+  })
+
   describe('addRoute returned function', () => {
     it('remove records', () => {
       const matcher = createRouterMatcher([], {})
@@ -26,8 +42,34 @@ describe('normalizeRouteRecord', () => {
     })
 
     it.todo('remove aliases')
-    it.todo('remove children')
     it.todo('remove aliases children')
+
+    it('remove children when removing the parent', () => {
+      const matcher = createRouterMatcher([], {})
+      const remove = matcher.addRoute({
+        path: '/',
+        component,
+        name: 'home',
+        children: [
+          // absolute path so it can work out
+          { path: '/about', name: 'child', component },
+        ],
+      })
+
+      remove()
+
+      expect(
+        matcher.resolve({ path: '/about' }, currentLocation)
+      ).toMatchObject({
+        name: undefined,
+        matched: [],
+      })
+
+      expect(matcher.getRecordMatcher('child')).toBe(undefined)
+      expect(() => {
+        matcher.resolve({ name: 'child' }, currentLocation)
+      }).toThrow()
+    })
   })
 
   it('can remove records by name', () => {
@@ -40,7 +82,7 @@ describe('normalizeRouteRecord', () => {
     })
   })
 
-  it('removes children', () => {
+  it('removes children when removing the parent', () => {
     const matcher = createRouterMatcher([], {})
     matcher.addRoute({
       path: '/',
@@ -48,7 +90,7 @@ describe('normalizeRouteRecord', () => {
       name: 'home',
       children: [
         // absolute path so it can work out
-        { path: '/about', component },
+        { path: '/about', name: 'child', component },
       ],
     })
 
@@ -57,9 +99,44 @@ describe('normalizeRouteRecord', () => {
       name: undefined,
       matched: [],
     })
+
+    expect(matcher.getRecordMatcher('child')).toBe(undefined)
+    expect(() => {
+      matcher.resolve({ name: 'child' }, currentLocation)
+    }).toThrow()
   })
 
   it.todo('removes alias by name')
-  it.todo('removes children by name')
+  it('removes children by name', () => {
+    const matcher = createRouterMatcher([], {})
+    matcher.addRoute({
+      path: '/',
+      component,
+      name: 'home',
+      children: [
+        // absolute path so it can work out
+        { path: '/about', name: 'child', component },
+      ],
+    })
+
+    matcher.removeRoute('child')
+
+    expect(matcher.resolve({ path: '/about' }, currentLocation)).toMatchObject({
+      name: undefined,
+      matched: [],
+    })
+
+    expect(matcher.getRecordMatcher('child')).toBe(undefined)
+    expect(() => {
+      matcher.resolve({ name: 'child' }, currentLocation)
+    }).toThrow()
+
+    expect(matcher.resolve({ path: '/' }, currentLocation)).toMatchObject({
+      name: 'home',
+    })
+  })
+
+  it.todo('removes children by name from parent')
+
   it.todo('removes children alias by name')
 })
index fde0f2d3cd4a38084403621f9741e6e5937ed403..088a272460028777845b4e23253c60ecbedc4935 100644 (file)
@@ -840,6 +840,50 @@ describe('Router Matcher', () => {
           }
         )
       })
+
+      it('resolves children with root as the parent', () => {
+        const Nested = { path: 'nested', name: 'nested', components }
+        const Parent = {
+          path: '/',
+          name: 'parent',
+          components,
+          children: [Nested],
+        }
+        assertRecordMatch(
+          Parent,
+          { path: '/nested' },
+          {
+            name: 'nested',
+            path: '/nested',
+            params: {},
+            matched: [Parent, { ...Nested, path: `/nested` }].map(
+              normalizeRouteRecord
+            ),
+          }
+        )
+      })
+
+      it('resolves children with parent with trailing slash', () => {
+        const Nested = { path: 'nested', name: 'nested', components }
+        const Parent = {
+          path: '/parent/',
+          name: 'parent',
+          components,
+          children: [Nested],
+        }
+        assertRecordMatch(
+          Parent,
+          { path: '/parent/nested' },
+          {
+            name: 'nested',
+            path: '/parent/nested',
+            params: {},
+            matched: [Parent, { ...Nested, path: `/parent/nested` }].map(
+              normalizeRouteRecord
+            ),
+          }
+        )
+      })
     })
   })
 })
index 7379db2fe60e060c8c1b887301b0acb9dfc64a94..f40234c3e630e588123c5b1b4a2623f530ddc26f 100644 (file)
@@ -23,8 +23,10 @@ interface RouterMatcher {
     (name: Required<RouteRecord>['name']): void
   }
   // TODO:
-  // getRoutes: () => RouteRecordMatcher
-  // hasRoute: (name: Required<RouteRecord>['name']) => boolean
+  // getRoutes: () => RouteRecordMatcher[]
+  getRecordMatcher: (
+    name: Required<RouteRecord>['name']
+  ) => RouteRecordMatcher | undefined
   resolve: (
     location: Readonly<MatcherLocation>,
     currentLocation: Readonly<MatcherLocationNormalized>
@@ -39,6 +41,10 @@ export function createRouterMatcher(
   const matchers: RouteRecordMatcher[] = []
   const matcherMap = new Map<string | symbol, RouteRecordMatcher>()
 
+  function getRecordMatcher(name: string) {
+    return matcherMap.get(name)
+  }
+
   function addRoute(
     record: Readonly<RouteRecord>,
     parent?: RouteRecordMatcher
@@ -63,10 +69,15 @@ export function createRouterMatcher(
 
     for (const normalizedRecord of normalizedRecords) {
       let { path } = normalizedRecord
-      // build up the path for nested routes if the child isn't an absolute route
-      // only add the / delimiter if the child path isn't empty
+      // Build up the path for nested routes if the child isn't an absolute
+      // route. Only add the / delimiter if the child path isn't empty and if the
+      // parent path doesn't have a trailing slash
       if (parent && path[0] !== '/') {
-        normalizedRecord.path = parent.record.path + (path && '/' + path)
+        let parentPath = parent.record.path
+        let connectingSlash =
+          parentPath[parentPath.length - 1] === '/' ? '' : '/'
+        normalizedRecord.path =
+          parent.record.path + (path && connectingSlash + path)
       }
 
       // create the object before hand so it can be passed to children
@@ -188,7 +199,7 @@ export function createRouterMatcher(
   // add initial routes
   routes.forEach(route => addRoute(route))
 
-  return { addRoute, resolve, removeRoute }
+  return { addRoute, resolve, removeRoute, getRecordMatcher }
 }
 
 /**