1- import Path from 'path-parser'
1+ import Path from 'path-parser' ;
22
3- let isSerialisable = val => val !== undefined && val !== null && val !== ''
3+ const isSerialisable = val => val !== undefined && val !== null && val !== '' ;
44
5- let removeQueryParamsFromPath = ( path , params ) => {
6- if ( path . indexOf ( '?' ) === - 1 ) return path
7- const splitPath = path . split ( '?' )
8- const pathPart = splitPath [ 0 ]
9- const searchPart = splitPath [ 1 ]
5+ const bracketTest = / \[ \] $ / ;
6+ const withoutBrackets = param => param . replace ( bracketTest , '' ) ;
7+
8+ const removeQueryParamsFromPath = ( path , params ) => {
9+ if ( path . indexOf ( '?' ) === - 1 ) return path ;
10+ const splitPath = path . split ( '?' ) ;
11+ const pathPart = splitPath [ 0 ] ;
12+ const searchPart = splitPath [ 1 ] ;
1013
1114 let remainingSearchParams = searchPart
1215 . split ( '&' )
1316 . reduce ( ( obj , p ) => {
14- const splitParam = p . split ( '=' )
15- const key = splitParam [ 0 ]
16- const val = decodeURIComponent ( splitParam [ 1 ] )
17- if ( params . indexOf ( key ) === - 1 ) obj [ key ] = val || ''
18- return obj
19- } , { } )
17+ const splitParam = p . split ( '=' ) ;
18+ const hasBrackets = bracketTest . test ( splitParam [ 0 ] ) ;
19+ const key = splitParam [ 0 ] ;
20+ let val = decodeURIComponent ( splitParam [ 1 ] ) ;
21+ val = hasBrackets ? [ val ] : val ;
22+
23+ if ( params . indexOf ( withoutBrackets ( key ) ) === - 1 ) obj [ key ] = val || '' ;
24+ return obj ;
25+ } , { } ) ;
2026
2127 let remainingSearchPart = Object . keys ( remainingSearchParams )
2228 . map ( p => [ p ] . concat ( isSerialisable ( remainingSearchParams [ p ] ) ? encodeURIComponent ( remainingSearchParams [ p ] ) : [ ] ) )
2329 . map ( p => p . join ( '=' ) )
24- . join ( '&' )
30+ . join ( '&' ) ;
2531
26- return pathPart + ( remainingSearchPart ? `?${ remainingSearchPart } ` : '' )
27- }
32+ return pathPart + ( remainingSearchPart ? `?${ remainingSearchPart } ` : '' ) ;
33+ } ;
2834
2935export default class RouteNode {
3036 constructor ( name = '' , path = '' , childRoutes = [ ] ) {
31- this . name = name
32- this . path = path
33- this . parser = path ? new Path ( path ) : null
34- this . children = [ ]
37+ this . name = name ;
38+ this . path = path ;
39+ this . parser = path ? new Path ( path ) : null ;
40+ this . children = [ ] ;
3541
36- this . add ( childRoutes )
42+ this . add ( childRoutes ) ;
3743
38- return this
44+ return this ;
3945 }
4046
4147 add ( route ) {
42- if ( route === undefined || route === null ) return
48+ if ( route === undefined || route === null ) return ;
4349
4450 if ( route instanceof Array ) {
45- route . forEach ( r => this . add ( r ) )
46- return
51+ route . forEach ( r => this . add ( r ) ) ;
52+ return ;
4753 }
4854
4955 if ( ! ( route instanceof RouteNode ) && ! ( route instanceof Object ) ) {
50- throw new Error ( 'RouteNode.add() expects routes to be an Object or an instance of RouteNode.' )
56+ throw new Error ( 'RouteNode.add() expects routes to be an Object or an instance of RouteNode.' ) ;
5157 }
5258 if ( route instanceof Object ) {
5359 if ( ! route . name || ! route . path ) {
54- throw new Error ( 'RouteNode.add() expects routes to have a name and a path defined.' )
60+ throw new Error ( 'RouteNode.add() expects routes to have a name and a path defined.' ) ;
5561 }
56- route = new RouteNode ( route . name , route . path , route . children )
62+ route = new RouteNode ( route . name , route . path , route . children ) ;
5763 }
5864 // Check duplicated routes
5965 if ( this . children . map ( child => child . name ) . indexOf ( route . name ) !== - 1 ) {
60- throw new Error ( `Alias "${ route . name } " is already defined in route node` )
66+ throw new Error ( `Alias "${ route . name } " is already defined in route node` ) ;
6167 }
6268 // Check duplicated paths
6369 if ( this . children . map ( child => child . path ) . indexOf ( route . path ) !== - 1 ) {
64- throw new Error ( `Path "${ route . path } " is already defined in route node` )
70+ throw new Error ( `Path "${ route . path } " is already defined in route node` ) ;
6571 }
6672
67- let names = route . name . split ( '.' )
73+ let names = route . name . split ( '.' ) ;
6874
6975 if ( names . length === 1 ) {
70- this . children . push ( route )
76+ this . children . push ( route ) ;
7177 // Push greedy spats to the bottom of the pile
7278 this . children . sort ( ( a , b ) => {
7379 // '/' last
7480 if ( a . path === '/' ) return 1 ;
7581 if ( b . path === '/' ) return - 1 ;
76- let aHasParams = a . parser . hasUrlParams || a . parser . hasSpatParam
77- let bHasParams = b . parser . hasUrlParams || b . parser . hasSpatParam
82+ let aHasParams = a . parser . hasUrlParams || a . parser . hasSpatParam ;
83+ let bHasParams = b . parser . hasUrlParams || b . parser . hasSpatParam ;
7884 // No params first, sort by length descending
7985 if ( ! aHasParams && ! bHasParams ) {
80- return a . path && b . path ? ( a . path . length < b . path . length ? 1 : - 1 ) : 0
86+ return a . path && b . path ? ( a . path . length < b . path . length ? 1 : - 1 ) : 0 ;
8187 }
8288 // Params last
83- if ( aHasParams && ! bHasParams ) return 1
84- if ( ! aHasParams && bHasParams ) return - 1
89+ if ( aHasParams && ! bHasParams ) return 1 ;
90+ if ( ! aHasParams && bHasParams ) return - 1 ;
8591 // Spat params last
86- if ( ! a . parser . hasSpatParam && b . parser . hasSpatParam ) return - 1
87- if ( ! b . parser . hasSpatParam && a . parser . hasSpatParam ) return 1
92+ if ( ! a . parser . hasSpatParam && b . parser . hasSpatParam ) return - 1 ;
93+ if ( ! b . parser . hasSpatParam && a . parser . hasSpatParam ) return 1 ;
8894 // Sort by number of segments descending
89- let aSegments = ( a . path . match ( / \/ / g) || [ ] ) . length
90- let bSegments = ( b . path . match ( / \/ / g) || [ ] ) . length
91- if ( aSegments < bSegments ) return 1
92- return 0
93- } )
95+ let aSegments = ( a . path . match ( / \/ / g) || [ ] ) . length ;
96+ let bSegments = ( b . path . match ( / \/ / g) || [ ] ) . length ;
97+ if ( aSegments < bSegments ) return 1 ;
98+ return 0 ;
99+ } ) ;
94100 } else {
95101 // Locate parent node
96- let segments = this . getSegmentsByName ( names . slice ( 0 , - 1 ) . join ( '.' ) )
102+ let segments = this . getSegmentsByName ( names . slice ( 0 , - 1 ) . join ( '.' ) ) ;
97103 if ( segments ) {
98- segments [ segments . length - 1 ] . add ( new RouteNode ( names [ names . length - 1 ] , route . path , route . children ) )
104+ segments [ segments . length - 1 ] . add ( new RouteNode ( names [ names . length - 1 ] , route . path , route . children ) ) ;
99105 } else {
100- throw new Error ( `Could not add route named '${ route . name } ', parent is missing.` )
106+ throw new Error ( `Could not add route named '${ route . name } ', parent is missing.` ) ;
101107 }
102108 }
103109
104- return this
110+ return this ;
105111 }
106112
107113 addNode ( name , params ) {
108- this . add ( new RouteNode ( name , params ) )
109- return this
114+ this . add ( new RouteNode ( name , params ) ) ;
115+ return this ;
110116 }
111117
112118 getSegmentsByName ( routeName ) {
113119 let findSegmentByName = ( name , routes ) => {
114- let filteredRoutes = routes . filter ( r => r . name === name )
115- return filteredRoutes . length ? filteredRoutes [ 0 ] : undefined
116- }
117- let segments = [ ]
120+ let filteredRoutes = routes . filter ( r => r . name === name ) ;
121+ return filteredRoutes . length ? filteredRoutes [ 0 ] : undefined ;
122+ } ;
123+ let segments = [ ] ;
118124 let names = routeName . split ( '.' ) ;
119- let routes = this . children
125+ let routes = this . children ;
120126
121127 let matched = names . every ( name => {
122- let segment = findSegmentByName ( name , routes )
128+ let segment = findSegmentByName ( name , routes ) ;
123129 if ( segment ) {
124- routes = segment . children
125- segments . push ( segment )
126- return true
130+ routes = segment . children ;
131+ segments . push ( segment ) ;
132+ return true ;
127133 }
128- return false
129- } )
134+ return false ;
135+ } ) ;
130136
131- return matched ? segments : null
137+ return matched ? segments : null ;
132138 }
133139
134140 getSegmentsMatchingPath ( path , options ) {
135141 const { trailingSlash, strictQueryParams } = options ;
136142 let matchChildren = ( nodes , pathSegment , segments ) => {
137143 // for (child of node.children) {
138144 for ( let i in nodes ) {
139- let child = nodes [ i ]
145+ let child = nodes [ i ] ;
140146 // Partially match path
141- let match = child . parser . partialMatch ( pathSegment )
142- let remainingPath , remainingSearch
147+ let match = child . parser . partialMatch ( pathSegment ) ;
148+ let remainingPath ;
143149
144150 if ( ! match && trailingSlash ) {
145151 // Try with optional trailing slash
146- match = child . parser . match ( pathSegment , true )
147- remainingPath = ''
152+ match = child . parser . match ( pathSegment , true ) ;
153+ remainingPath = '' ;
148154 } else if ( match ) {
149155 // Remove consumed segment from path
150- let consumedPath = child . parser . build ( match , { ignoreSearch : true } )
151- remainingPath = removeQueryParamsFromPath ( pathSegment . replace ( consumedPath , '' ) , child . parser . queryParams )
156+ let consumedPath = child . parser . build ( match , { ignoreSearch : true } ) ;
157+ remainingPath = removeQueryParamsFromPath ( pathSegment . replace ( consumedPath , '' ) , child . parser . queryParams . concat ( child . parser . queryParamsBr ) ) ;
152158
153159 if ( trailingSlash && remainingPath === '/' && ! / \/ $ / . test ( consumedPath ) ) {
154- remainingPath = ''
160+ remainingPath = '' ;
155161 }
156162 }
157163
158164 if ( match ) {
159- segments . push ( child )
160- Object . keys ( match ) . forEach ( param => segments . params [ param ] = match [ param ] )
165+ segments . push ( child ) ;
166+ Object . keys ( match ) . forEach ( param => segments . params [ param ] = match [ param ] ) ;
161167
162168 if ( ! remainingPath . length || // fully matched
163169 ! strictQueryParams && remainingPath . indexOf ( '?' ) === 0 // unmatched queryParams in non strict mode
164170 ) {
165- return segments
171+ return segments ;
166172 }
167173 // If no children to match against but unmatched path left
168174 if ( ! child . children . length ) {
169- return null
175+ return null ;
170176 }
171177 // Else: remaining path and children
172178 return matchChildren ( child . children , remainingPath , segments ) ;
173179 }
174180 }
175181 return null ;
176- }
182+ } ;
177183
178- let startingNodes = this . parser ? [ this ] : this . children
179- let segments = [ ]
180- segments . params = { }
184+ let startingNodes = this . parser ? [ this ] : this . children ;
185+ let segments = [ ] ;
186+ segments . params = { } ;
181187
182- return matchChildren ( startingNodes , path , segments )
188+ return matchChildren ( startingNodes , path , segments ) ;
183189 }
184190
185191 getPathFromSegments ( segments ) {
186- return segments ? segments . map ( segment => segment . path ) . join ( '' ) : null
192+ return segments ? segments . map ( segment => segment . path ) . join ( '' ) : null ;
187193 }
188194
189195 getPath ( routeName ) {
190- return this . getPathFromSegments ( this . getSegmentsByName ( routeName ) )
196+ return this . getPathFromSegments ( this . getSegmentsByName ( routeName ) ) ;
191197 }
192198
193199 buildPathFromSegments ( segments , params = { } ) {
194- if ( ! segments ) return null
200+ if ( ! segments ) return null ;
195201
196- let searchParams = segments
202+ const searchParams = segments
197203 . filter ( s => s . parser . hasQueryParams )
198- . map ( s => s . parser . queryParams ) ;
199-
200- let searchPart = ! searchParams . length ? null : searchParams
201- . reduce ( ( queryParams , params ) => queryParams . concat ( params ) )
202- . filter ( p => Object . keys ( params ) . indexOf ( p ) !== - 1 )
203- . map ( p => Path . serialise ( p , params [ p ] ) )
204- . join ( '&' )
205-
206- return segments . map ( segment => segment . parser . build ( params , { ignoreSearch : true } ) ) . join ( '' ) + ( searchPart ? '?' + searchPart : '' )
204+ . reduce (
205+ ( params , s ) => params
206+ . concat ( s . parser . queryParams )
207+ . concat ( s . parser . queryParamsBr . map ( p => p + '[]' ) ) ,
208+ [ ]
209+ ) ;
210+
211+ const searchPart = ! searchParams . length ? null : searchParams
212+ . filter ( p => Object . keys ( params ) . indexOf ( withoutBrackets ( p ) ) !== - 1 )
213+ . map ( p => Path . serialise ( p , params [ withoutBrackets ( p ) ] ) )
214+ . join ( '&' ) ;
215+
216+ return segments
217+ . map ( segment => segment . parser . build ( params , { ignoreSearch : true } ) )
218+ . join ( '' ) + ( searchPart ? '?' + searchPart : '' ) ;
207219 }
208220
209221 getMetaFromSegments ( segments ) {
@@ -227,7 +239,7 @@ export default class RouteNode {
227239 }
228240
229241 buildPath ( routeName , params = { } ) {
230- return this . buildPathFromSegments ( this . getSegmentsByName ( routeName ) , params )
242+ return this . buildPathFromSegments ( this . getSegmentsByName ( routeName ) , params ) ;
231243 }
232244
233245 buildStateFromSegments ( segments ) {
@@ -257,6 +269,6 @@ export default class RouteNode {
257269 matchPath ( path , options ) {
258270 const defaultOptions = { trailingSlash : false , strictQueryParams : true } ;
259271 options = { ...defaultOptions , ...options } ;
260- return this . buildStateFromSegments ( this . getSegmentsMatchingPath ( path , options ) )
272+ return this . buildStateFromSegments ( this . getSegmentsMatchingPath ( path , options ) ) ;
261273 }
262274}
0 commit comments