]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
feat: add children suppor to matcher
authorEduardo San Martin Morote <posva13@gmail.com>
Mon, 6 May 2019 13:42:48 +0000 (15:42 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Mon, 6 May 2019 13:42:48 +0000 (15:42 +0200)
__tests__/matcher.spec.js
src/matcher.ts
src/types/index.ts

index 42a0b9a4a488e2e45e1a8a4b0a551ca7e56168c8..b714866dd7eb9fa0c23b478fcf5a5412ea347341 100644 (file)
@@ -445,5 +445,65 @@ describe('Router Matcher', () => {
         )
       })
     })
+
+    describe('children', () => {
+      const ChildA = { path: 'a', name: 'child-a', component }
+      const ChildB = { path: 'b', name: 'child-b', component }
+      const ChildC = { path: 'c', name: 'child-c', component }
+      const NestedChildA = { ...ChildA, name: 'nested-child-a' }
+      const NestedChildB = { ...ChildB, name: 'nested-child-b' }
+      const NestedChildC = { ...ChildC, name: 'nested-child-c' }
+      const Nested = {
+        path: 'nested',
+        name: 'nested',
+        component,
+        children: [NestedChildA, NestedChildB, NestedChildC],
+      }
+
+      it('resolves children', () => {
+        const Foo = {
+          path: '/foo',
+          name: 'Foo',
+          component,
+          children: [ChildA, ChildB, ChildC],
+        }
+        assertRecordMatch(
+          Foo,
+          { path: '/foo/b' },
+          {
+            name: 'child-b',
+            path: '/foo/b',
+            params: {},
+            matched: [Foo, { ...ChildB, path: `${Foo.path}/${ChildB.path}` }],
+          }
+        )
+      })
+
+      it('resolves nested children', () => {
+        const Foo = {
+          path: '/foo',
+          name: 'Foo',
+          component,
+          children: [Nested],
+        }
+        assertRecordMatch(
+          Foo,
+          { path: '/foo/nested/a' },
+          {
+            name: 'nested-child-a',
+            path: '/foo/nested/a',
+            params: {},
+            matched: [
+              Foo,
+              { ...Nested, path: `${Foo.path}/${Nested.path}` },
+              {
+                ...NestedChildA,
+                path: `${Foo.path}/${Nested.path}/${NestedChildA.path}`,
+              },
+            ],
+          }
+        )
+      })
+    })
   })
 })
index 46baa6a3beac3fcce4ce6376ae29a0047134f57a..14845626d8057b885c97ab1a77601a785a65574f 100644 (file)
@@ -11,27 +11,77 @@ import { NoRouteMatchError, InvalidRouteMatch } from './errors'
 interface RouteMatcher {
   re: RegExp
   resolve: (params?: RouteParams) => string
-  record: RouteRecord
+  record: RouteRecord // TODO: NormalizedRouteRecord?
+  parent: RouteMatcher | void
   keys: string[]
 }
 
-function generateMatcher(record: RouteRecord): RouteMatcher {
-  const keys: pathToRegexp.Key[] = []
-  // TODO: if children use option end: false ?
-  const re = pathToRegexp(record.path, keys)
-  return {
-    re,
-    resolve: pathToRegexp.compile(record.path),
-    keys: keys.map(k => '' + k.name),
-    record,
-  }
-}
+// function generateMatcher(record: RouteRecord): RouteMatcher {
+//   const keys: pathToRegexp.Key[] = []
+
+//   const options: pathToRegexp.RegExpOptions = {}
+//   let children: RouteMatcher[] = []
+//   // TODO: if children use option end: false ?
+//   // TODO: why is the isArray check necessary for ts?
+//   if ('children' in record && Array.isArray(record.children)) {
+//     children = record.children.map(generateMatcher)
+//     options.end = false // match for partial url
+//   }
+
+//   const re = pathToRegexp(record.path, keys, options)
+
+//   return {
+//     re,
+//     resolve: pathToRegexp.compile(record.path),
+//     keys: keys.map(k => '' + k.name),
+//     record,
+//   }
+// }
 
 export class RouterMatcher {
   private matchers: RouteMatcher[] = []
 
   constructor(routes: RouteRecord[]) {
-    this.matchers = routes.map(generateMatcher)
+    for (const route of routes) {
+      this.addRouteRecord(route)
+    }
+  }
+
+  private addRouteRecord(
+    record: Readonly<RouteRecord>,
+    parent?: RouteMatcher
+  ): void {
+    const keys: pathToRegexp.Key[] = []
+    const options: pathToRegexp.RegExpOptions = {}
+
+    const recordCopy = { ...record }
+    if (parent) {
+      // if the child isn't an absolute route
+      if (record.path[0] !== '/') {
+        recordCopy.path = parent.record.path + '/' + record.path // TODO: check for trailing slash?
+      }
+    }
+
+    const re = pathToRegexp(recordCopy.path, keys, options)
+
+    // create the object before hand so it can be passed to children
+    const matcher: RouteMatcher = {
+      parent,
+      record: recordCopy,
+      re,
+      keys: keys.map(k => '' + k.name),
+      resolve: pathToRegexp.compile(record.path),
+    }
+
+    // TODO: if children use option end: false ?
+    // TODO: why is the isArray check necessary for ts?
+    if ('children' in record && Array.isArray(record.children)) {
+      for (const childRecord of record.children) {
+        this.addRouteRecord(childRecord, matcher)
+      }
+    }
+
+    this.matchers.push(matcher)
   }
 
   /**
@@ -88,8 +138,16 @@ export class RouterMatcher {
         }
       }
 
-      // TODO: build up the array with children based on current location
-      const matched = [matcher.record]
+      // put the parents of the matched record first
+      const matched: MatcherLocationNormalized['matched'] = [matcher.record]
+      let parentMatcher: RouteMatcher | void = matcher.parent
+      while (parentMatcher) {
+        // reversed order so parents are at the beginning
+        // TODO: should be doable by typing RouteMatcher in a different way
+        if ('redirect' in parentMatcher.record) throw new Error('TODO')
+        matched.unshift(parentMatcher.record)
+        parentMatcher = parentMatcher.parent
+      }
 
       return {
         name: matcher.record.name,
index 0aa8e254dae08be2233b078d4ba8779a96365132..cbee6c39a11530ff04f6e356bfbd43a4924e2a7e 100644 (file)
@@ -109,6 +109,7 @@ export interface RouteRecordRedirect extends RouteRecordCommon {
 
 interface RouteRecordSingleView extends RouteRecordCommon {
   component: RouteComponent | Lazy<RouteComponent>
+  children?: RouteRecord[]
 }
 
 interface RouteRecordMultipleViews extends RouteRecordCommon {