@@ -4,17 +4,62 @@ import { getSearch, getPath, omit, withoutBrackets, parse } from 'search-params'
44const noop = ( ) => { } ;
55
66export default class RouteNode {
7- constructor ( name = '' , path = '' , childRoutes = [ ] , cb ) {
7+ constructor ( name = '' , path = '' , childRoutes = [ ] , cb , parent ) {
88 this . name = name ;
9- this . path = path ;
10- this . parser = path ? new Path ( path ) : null ;
9+ this . absolute = / ^ ~ / . test ( path ) ;
10+ this . path = this . absolute ? path . slice ( 1 ) : path ;
11+ this . parser = this . path ? new Path ( this . path ) : null ;
1112 this . children = [ ] ;
13+ this . parent = parent ;
14+
15+ this . checkParents ( ) ;
1216
1317 this . add ( childRoutes , cb ) ;
1418
1519 return this ;
1620 }
1721
22+ checkParents ( ) {
23+ if ( this . absolute && this . haveParentsParams ( ) ) {
24+ throw new Error ( '[RouteNode] A RouteNode with an abolute path cannot have parents with route parameters' ) ;
25+ }
26+ }
27+
28+ haveParentsParams ( ) {
29+ if ( this . parent && this . parent . parser ) {
30+ const parser = this . parent . parser ;
31+ const hasParams = parser . hasUrlParams || parser . hasSpatParam || parser . hasMatrixParams || parser . hasQueryParams ;
32+
33+ return hasParams || this . parent . haveParentsParams ( ) ;
34+ }
35+
36+ return false ;
37+ }
38+
39+ getNonAbsoluteChildren ( ) {
40+ return this . children . filter ( ( child ) => ! child . absolute ) ;
41+ }
42+
43+ findAbsoluteChildren ( ) {
44+ return this . children . reduce ( ( absoluteChildren , child ) =>
45+ absoluteChildren
46+ . concat ( child . absolute ? child : [ ] )
47+ . concat ( child . findAbsoluteChildren ( ) ) ,
48+ [ ]
49+ ) ;
50+ }
51+
52+ getParentSegments ( segments = [ ] ) {
53+ return this . parent && this . parent . parser
54+ ? this . parent . getParentSegments ( segments . concat ( this . parent ) )
55+ : segments . reverse ( ) ;
56+ }
57+
58+ setParent ( parent ) {
59+ this . parent = parent ;
60+ this . checkParents ( ) ;
61+ }
62+
1863 setPath ( path = '' ) {
1964 this . path = path ;
2065 this . parser = path ? new Path ( path ) : null ;
@@ -31,13 +76,14 @@ export default class RouteNode {
3176
3277 if ( ! ( route instanceof RouteNode ) && ! ( route instanceof Object ) ) {
3378 throw new Error ( 'RouteNode.add() expects routes to be an Object or an instance of RouteNode.' ) ;
34- }
35- if ( route instanceof Object ) {
79+ } else if ( route instanceof RouteNode ) {
80+ route . setParent ( this ) ;
81+ } else {
3682 if ( ! route . name || ! route . path ) {
3783 throw new Error ( 'RouteNode.add() expects routes to have a name and a path defined.' ) ;
3884 }
3985 originalRoute = route ;
40- route = new RouteNode ( route . name , route . path , route . children , cb ) ;
86+ route = new RouteNode ( route . name , route . path , route . children , cb , this ) ;
4187 }
4288
4389 let names = route . name . split ( '.' ) ;
@@ -167,19 +213,26 @@ export default class RouteNode {
167213 remainingQueryParams . forEach ( ( { name, value} ) => segments . params [ name ] = value ) ;
168214 return segments ;
169215 }
216+ // Continue matching on non absolute children
217+ const children = child . getNonAbsoluteChildren ( ) ;
170218 // If no children to match against but unmatched path left
171- if ( ! child . children . length ) {
219+ if ( ! children . length ) {
172220 return null ;
173221 }
174222 // Else: remaining path and children
175- return matchChildren ( child . children , remainingPath , segments ) ;
223+ return matchChildren ( children , remainingPath , segments ) ;
176224 }
177225 }
178226
179227 return null ;
180228 } ;
181229
182- let startingNodes = this . parser ? [ this ] : this . children ;
230+ const topLevelNodes = this . parser ? [ this ] : this . children ;
231+ const startingNodes = topLevelNodes . reduce (
232+ ( nodes , node ) => nodes . concat ( node , node . findAbsoluteChildren ( ) ) ,
233+ [ ]
234+ ) ;
235+
183236 let segments = [ ] ;
184237 segments . params = { } ;
185238
@@ -228,9 +281,14 @@ export default class RouteNode {
228281 } )
229282 . join ( '&' ) ;
230283
231- return segments
232- . map ( segment => segment . parser . build ( params , { ignoreSearch : true } ) )
233- . join ( '' ) + ( searchPart ? '?' + searchPart : '' ) ;
284+ const path = segments
285+ . reduce ( ( path , segment ) => {
286+ const segmentPath = segment . parser . build ( params , { ignoreSearch : true } ) ;
287+
288+ return segment . absolute ? segmentPath : path + segmentPath ;
289+ } , '' ) ;
290+
291+ return path + ( searchPart ? '?' + searchPart : '' ) ;
234292 }
235293
236294 getMetaFromSegments ( segments ) {
@@ -288,7 +346,17 @@ export default class RouteNode {
288346
289347 matchPath ( path , options ) {
290348 const defaultOptions = { trailingSlash : false , strictQueryParams : true } ;
291- options = { ...defaultOptions , ...options } ;
292- return this . buildStateFromSegments ( this . getSegmentsMatchingPath ( path , options ) ) ;
349+ const opts = { ...defaultOptions , ...options } ;
350+ let matchedSegments = this . getSegmentsMatchingPath ( path , opts ) ;
351+
352+ if ( matchedSegments && matchedSegments [ 0 ] . absolute ) {
353+ const firstSegmentParams = matchedSegments [ 0 ] . getParentSegments ( ) ;
354+
355+ matchedSegments . reverse ( ) ;
356+ matchedSegments . push ( ...firstSegmentParams ) ;
357+ matchedSegments . reverse ( ) ;
358+ }
359+
360+ return this . buildStateFromSegments ( matchedSegments ) ;
293361 }
294362}
0 commit comments