@@ -117,6 +117,24 @@ export function convertToParamMap(params: Params): ParamMap {
117117 return new ParamsAsMap ( params ) ;
118118}
119119
120+ function matchParts (
121+ routeParts : string [ ] ,
122+ urlSegments : UrlSegment [ ] ,
123+ posParams : { [ key : string ] : UrlSegment } ,
124+ ) : boolean {
125+ for ( let i = 0 ; i < routeParts . length ; i ++ ) {
126+ const part = routeParts [ i ] ;
127+ const segment = urlSegments [ i ] ;
128+ const isParameter = part [ 0 ] === ':' ;
129+ if ( isParameter ) {
130+ posParams [ part . substring ( 1 ) ] = segment ;
131+ } else if ( part !== segment . path ) {
132+ return false ;
133+ }
134+ }
135+ return true ;
136+ }
137+
120138/**
121139 * Matches the route configuration (`route`) against the actual URL (`segments`).
122140 *
@@ -138,34 +156,73 @@ export function defaultUrlMatcher(
138156 route : Route ,
139157) : UrlMatchResult | null {
140158 const parts = route . path ! . split ( '/' ) ;
159+ const wildcardIndex = parts . indexOf ( '**' ) ;
160+ if ( wildcardIndex === - 1 ) {
161+ // No wildcard, use original logic
162+ if ( parts . length > segments . length ) {
163+ // The actual URL is shorter than the config, no match
164+ return null ;
165+ }
141166
142- if ( parts . length > segments . length ) {
143- // The actual URL is shorter than the config, no match
167+ if (
168+ route . pathMatch === 'full' &&
169+ ( segmentGroup . hasChildren ( ) || parts . length < segments . length )
170+ ) {
171+ // The config is longer than the actual URL but we are looking for a full match, return null
172+ return null ;
173+ }
174+
175+ const posParams : { [ key : string ] : UrlSegment } = { } ;
176+ const consumed = segments . slice ( 0 , parts . length ) ;
177+ if ( ! matchParts ( parts , consumed , posParams ) ) {
178+ return null ;
179+ }
180+ return { consumed, posParams} ;
181+ }
182+
183+ // Path has a wildcard.
184+ if ( wildcardIndex !== parts . lastIndexOf ( '**' ) ) {
185+ // We do not support more than one wildcard segment in the path
144186 return null ;
145187 }
146188
189+ const pre = parts . slice ( 0 , wildcardIndex ) ;
190+ const post = parts . slice ( wildcardIndex + 1 ) ;
191+
192+ if ( pre . length + post . length > segments . length ) {
193+ // The actual URL is shorter than the config, no match
194+ return null ;
195+ }
147196 if (
148- route . pathMatch === 'full' &&
149- ( segmentGroup . hasChildren ( ) || parts . length < segments . length )
197+ // If the wildcard is not at the end of the path, it must match at least one segment.
198+ // e.g. `foo/**/bar` does not match `foo/bar`.
199+ wildcardIndex > - 1 &&
200+ pre . length > 0 &&
201+ post . length > 0 &&
202+ pre . length + post . length === segments . length
150203 ) {
204+ return null ;
205+ }
206+
207+ if ( route . pathMatch === 'full' && segmentGroup . hasChildren ( ) && route . path !== '**' ) {
151208 // The config is longer than the actual URL but we are looking for a full match, return null
152209 return null ;
153210 }
154211
155212 const posParams : { [ key : string ] : UrlSegment } = { } ;
156213
157- // Check each config part against the actual URL
158- for ( let index = 0 ; index < parts . length ; index ++ ) {
159- const part = parts [ index ] ;
160- const segment = segments [ index ] ;
161- const isParameter = part [ 0 ] === ':' ;
162- if ( isParameter ) {
163- posParams [ part . substring ( 1 ) ] = segment ;
164- } else if ( part !== segment . path ) {
165- // The actual URL part does not match the config, no match
166- return null ;
167- }
214+ // Match the segments before the wildcard
215+ if ( ! matchParts ( pre , segments . slice ( 0 , pre . length ) , posParams ) ) {
216+ return null ;
217+ }
218+ // Match the segments after the wildcard
219+ if ( ! matchParts ( post , segments . slice ( segments . length - post . length ) , posParams ) ) {
220+ return null ;
168221 }
169222
170- return { consumed : segments . slice ( 0 , parts . length ) , posParams} ;
223+ // TODO(atscott): put the wildcard segments into a _splat param.
224+ // this would require a breaking change to the UrlMatchResult to allow UrlSegment[]
225+ // since the splat could be multiple segments.
226+
227+ return { consumed : segments , posParams} ;
171228}
0 commit comments