@@ -81,7 +81,7 @@ type TokenType =
8181interface LexToken {
8282 type : TokenType ;
8383 index : number ;
84- value : string ;
84+ value : string | { name : string ; pattern ?: string } ;
8585}
8686
8787const SIMPLE_TOKENS : Record < string , TokenType > = {
@@ -119,7 +119,10 @@ function* lexer(str: string): Generator<LexToken, LexToken> {
119119 const chars = [ ...str ] ;
120120 let i = 0 ;
121121
122- function name ( ) {
122+ function name ( options ?: { pattern ?: boolean } ) : {
123+ name : string ;
124+ pattern ?: string ;
125+ } {
123126 let value = "" ;
124127
125128 if ( ID_START . test ( chars [ ++ i ] ) ) {
@@ -153,7 +156,29 @@ function* lexer(str: string): Generator<LexToken, LexToken> {
153156 throw new TypeError ( `Missing parameter name at ${ i } : ${ DEBUG_URL } ` ) ;
154157 }
155158
156- return value ;
159+ if ( chars [ i ] === "(" && options ?. pattern ) {
160+ let depth = 1 ;
161+ let pattern = "" ;
162+ i ++ ;
163+ while ( i < chars . length && depth > 0 ) {
164+ if ( chars [ i ] === "(" ) {
165+ depth ++ ;
166+ } else if ( chars [ i ] === ")" ) {
167+ depth -- ;
168+ }
169+ if ( depth > 0 ) {
170+ pattern += chars [ i ++ ] ;
171+ }
172+ }
173+ if ( depth !== 0 ) {
174+ throw new TypeError (
175+ `Unterminated parameter pattern at ${ i } : ${ DEBUG_URL } ` ,
176+ ) ;
177+ }
178+ i ++ ;
179+ return { name : value , pattern } ;
180+ }
181+ return { name : value } ;
157182 }
158183
159184 while ( i < chars . length ) {
@@ -165,10 +190,14 @@ function* lexer(str: string): Generator<LexToken, LexToken> {
165190 } else if ( value === "\\" ) {
166191 yield { type : "ESCAPED" , index : i ++ , value : chars [ i ++ ] } ;
167192 } else if ( value === ":" ) {
168- const value = name ( ) ;
169- yield { type : "PARAM" , index : i , value } ;
193+ const value = name ( { pattern : true } ) ;
194+ yield {
195+ type : "PARAM" ,
196+ index : i ,
197+ value,
198+ } ;
170199 } else if ( value === "*" ) {
171- const value = name ( ) ;
200+ const { name : value } = name ( ) ;
172201 yield { type : "WILDCARD" , index : i , value } ;
173202 } else {
174203 yield { type : "CHAR" , index : i , value : chars [ i ++ ] } ;
@@ -191,15 +220,23 @@ class Iter {
191220 return this . _peek ;
192221 }
193222
194- tryConsume ( type : TokenType ) : string | undefined {
223+ tryConsume ( type : Extract < TokenType , "PARAM" > ) : {
224+ name : string ;
225+ pattern ?: string ;
226+ } ;
227+ tryConsume ( type : Exclude < TokenType , "PARAM" > ) : string ;
228+ tryConsume (
229+ type : TokenType ,
230+ ) : string | { name : string ; pattern ?: string } | undefined {
195231 const token = this . peek ( ) ;
196232 if ( token . type !== type ) return ;
197233 this . _peek = undefined ; // Reset after consumed.
198234 return token . value ;
199235 }
200236
201- consume ( type : TokenType ) : string {
202- const value = this . tryConsume ( type ) ;
237+ consume ( type : TokenType ) : string | { name : string ; pattern ?: string } {
238+ const value =
239+ type === "PARAM" ? this . tryConsume ( type ) : this . tryConsume ( type ) ;
203240 if ( value !== undefined ) return value ;
204241 const { type : nextType , index } = this . peek ( ) ;
205242 throw new TypeError (
@@ -231,6 +268,7 @@ export interface Text {
231268export interface Parameter {
232269 type : "param" ;
233270 name : string ;
271+ pattern ?: string ;
234272}
235273
236274/**
@@ -287,9 +325,11 @@ export function parse(str: string, options: ParseOptions = {}): TokenData {
287325
288326 const param = it . tryConsume ( "PARAM" ) ;
289327 if ( param ) {
328+ const { name, pattern } = param ;
290329 tokens . push ( {
291330 type : "param" ,
292- name : param ,
331+ name,
332+ pattern,
293333 } ) ;
294334 continue ;
295335 }
@@ -579,7 +619,14 @@ function toRegExp(tokens: Flattened[], delimiter: string, keys: Keys) {
579619 }
580620
581621 if ( token . type === "param" ) {
582- result += `(${ negate ( delimiter , isSafeSegmentParam ? "" : backtrack ) } +)` ;
622+ if ( token . pattern ) {
623+ result += `(${ token . pattern } )` ;
624+ } else {
625+ result += `(${ negate (
626+ delimiter ,
627+ isSafeSegmentParam ? "" : backtrack ,
628+ ) } +)`;
629+ }
583630 } else {
584631 result += `([\\s\\S]+)` ;
585632 }
0 commit comments