@@ -903,7 +903,7 @@ export function matchPath<
903
903
pattern = { path : pattern , caseSensitive : false , end : true } ;
904
904
}
905
905
906
- let [ matcher , paramNames ] = compilePath (
906
+ let [ matcher , compiledParams ] = compilePath (
907
907
pattern . path ,
908
908
pattern . caseSensitive ,
909
909
pattern . end
@@ -915,8 +915,8 @@ export function matchPath<
915
915
let matchedPathname = match [ 0 ] ;
916
916
let pathnameBase = matchedPathname . replace ( / ( .) \/ + $ / , "$1" ) ;
917
917
let captureGroups = match . slice ( 1 ) ;
918
- let params : Params = paramNames . reduce < Mutable < Params > > (
919
- ( memo , paramName , index ) => {
918
+ let params : Params = compiledParams . reduce < Mutable < Params > > (
919
+ ( memo , { paramName, isOptional } , index ) => {
920
920
// We need to compute the pathnameBase here using the raw splat value
921
921
// instead of using params["*"] later because it will be decoded then
922
922
if ( paramName === "*" ) {
@@ -926,10 +926,12 @@ export function matchPath<
926
926
. replace ( / ( .) \/ + $ / , "$1" ) ;
927
927
}
928
928
929
- memo [ paramName ] = safelyDecodeURIComponent (
930
- captureGroups [ index ] || "" ,
931
- paramName
932
- ) ;
929
+ const value = captureGroups [ index ] ;
930
+ if ( isOptional && ! value ) {
931
+ memo [ paramName ] = undefined ;
932
+ } else {
933
+ memo [ paramName ] = safelyDecodeURIComponent ( value || "" , paramName ) ;
934
+ }
933
935
return memo ;
934
936
} ,
935
937
{ }
@@ -943,11 +945,13 @@ export function matchPath<
943
945
} ;
944
946
}
945
947
948
+ type CompiledPathParam = { paramName : string ; isOptional ?: boolean } ;
949
+
946
950
function compilePath (
947
951
path : string ,
948
952
caseSensitive = false ,
949
953
end = true
950
- ) : [ RegExp , string [ ] ] {
954
+ ) : [ RegExp , CompiledPathParam [ ] ] {
951
955
warning (
952
956
path === "*" || ! path . endsWith ( "*" ) || path . endsWith ( "/*" ) ,
953
957
`Route path "${ path } " will be treated as if it were ` +
@@ -956,20 +960,20 @@ function compilePath(
956
960
`please change the route path to "${ path . replace ( / \* $ / , "/*" ) } ".`
957
961
) ;
958
962
959
- let paramNames : string [ ] = [ ] ;
963
+ let params : CompiledPathParam [ ] = [ ] ;
960
964
let regexpSource =
961
965
"^" +
962
966
path
963
967
. replace ( / \/ * \* ? $ / , "" ) // Ignore trailing / and /*, we'll handle it below
964
968
. replace ( / ^ \/ * / , "/" ) // Make sure it has a leading /
965
- . replace ( / [ \\ . * + ^ $ ? { } | ( ) [ \] ] / g, "\\$&" ) // Escape special regex chars
966
- . replace ( / \/ : ( \w + ) / g, ( _ : string , paramName : string ) => {
967
- paramNames . push ( paramName ) ;
968
- return "/([^\\/]+)" ;
969
+ . replace ( / [ \\ . * + ^ $ { } | ( ) [ \] ] / g, "\\$&" ) // Escape special regex chars
970
+ . replace ( / \/ : ( \w + ) ( \? ) ? / g, ( _ : string , paramName : string , isOptional ) => {
971
+ params . push ( { paramName, isOptional : isOptional != null } ) ;
972
+ return isOptional ? "/?([^\\/]+)?" : "/([^\\/]+)" ;
969
973
} ) ;
970
974
971
975
if ( path . endsWith ( "*" ) ) {
972
- paramNames . push ( "*" ) ;
976
+ params . push ( { paramName : "*" } ) ;
973
977
regexpSource +=
974
978
path === "*" || path === "/*"
975
979
? "(.*)$" // Already matched the initial /, just match the rest
@@ -992,7 +996,7 @@ function compilePath(
992
996
993
997
let matcher = new RegExp ( regexpSource , caseSensitive ? undefined : "i" ) ;
994
998
995
- return [ matcher , paramNames ] ;
999
+ return [ matcher , params ] ;
996
1000
}
997
1001
998
1002
function safelyDecodeURI ( value : string ) {
0 commit comments