@@ -165,6 +165,8 @@ program
165165 }
166166 }
167167 } ;
168+ // dynamic route rule
169+ const dynamicRouteRule = / \/ : / ;
168170 // http server
169171 const startHttpServer = async ( ) => {
170172 const suchStoreConfig = cliSuch . store ( "config" ) ;
@@ -217,22 +219,118 @@ program
217219 ? ( ) => timeout [ 0 ] + Math . round ( Math . random ( ) * timeout [ 1 ] )
218220 : ( ) => timeout || 0 ;
219221 const genTimeout = genTimeoutHandle ( timeout ) ;
222+ // dynamic helper handles
223+ const getDynamicRoutePatterns = ( pathname ) => {
224+ const segments = trimPathnameLeft ( pathname ) . split ( "/" ) ;
225+ const pathSegs = [ ] ;
226+ const patterns = [ ] ;
227+ for ( const seg of segments ) {
228+ if ( seg . startsWith ( ":" ) ) {
229+ let len = seg . length ;
230+ let optional = false ;
231+ if ( seg . endsWith ( "?" ) ) {
232+ optional = true ;
233+ len -- ;
234+ }
235+ const param = seg . slice ( 1 , len ) ;
236+ patterns . push ( {
237+ param,
238+ optional,
239+ } ) ;
240+ pathSegs . push ( `_${ param } ` ) ;
241+ } else {
242+ patterns . push ( {
243+ path : seg ,
244+ optional : false ,
245+ } ) ;
246+ pathSegs . push ( seg ) ;
247+ }
248+ }
249+ return {
250+ pathSegs,
251+ patterns,
252+ } ;
253+ } ;
254+ const buildDynamicMatchFn = ( pathname , patterns ) => {
255+ if ( dynamicMatchFns [ pathname ] ) {
256+ return dynamicMatchFns [ pathname ] ;
257+ }
258+ if ( ! patterns ) {
259+ patterns = getDynamicRoutePatterns ( pathname ) . patterns ;
260+ }
261+ return ( dynamicMatchFns [ pathname ] = ( segs ) => {
262+ const params = { } ;
263+ for ( const [ index , seg ] of Object . entries ( segs ) ) {
264+ const pattern = patterns [ index ] || { } ;
265+ if ( hasOwn ( pattern , "path" ) ) {
266+ if ( pattern . path === seg ) {
267+ continue ;
268+ }
269+ } else if ( hasOwn ( pattern , "param" ) ) {
270+ params [ pattern . param ] = seg ;
271+ continue ;
272+ }
273+ return {
274+ matched : false ,
275+ params,
276+ } ;
277+ }
278+ if ( patterns . length > segs . length ) {
279+ const leavePatterns = patterns . slice ( segs . length ) ;
280+ for ( const pattern of leavePatterns ) {
281+ if ( ! pattern . optional ) {
282+ return {
283+ matched : false ,
284+ params,
285+ } ;
286+ }
287+ }
288+ }
289+ return {
290+ matched : true ,
291+ params,
292+ } ;
293+ } ) ;
294+ } ;
295+ const dynamicMatchFns = { } ;
296+ // do with the prefix
220297 const hasPrefixExclude = Array . isArray ( prefix ) ;
221298 // find and remove the prefix
222299 const matchPrefix = prefix
223300 ? hasPrefixExclude
224301 ? ( pathname , method ) => {
225302 const [ curPrefix , { exclude = [ ] } = { } ] = prefix ;
303+ let params = { } ;
226304 // check if is in exclude
227305 const isInExclude = exclude . some ( ( item ) => {
306+ let isDynamic = false ;
307+ let excludePathname ;
228308 if ( typeof item === "string" ) {
229- return pathname === item ;
309+ if ( dynamicRouteRule . test ( item ) ) {
310+ isDynamic = true ;
311+ excludePathname = item ;
312+ } else {
313+ return pathname === item ;
314+ }
230315 } else if ( isObject ( item ) && item . path ) {
231316 let isMatch = false ;
232- if ( typeof item . path === "string" ) {
233- isMatch = item . path === pathname ;
234- } else if ( item . path instanceof RegExp ) {
235- isMatch = item . path . test ( pathname ) ;
317+ const curPath = item . path ;
318+ if ( typeof curPath === "string" ) {
319+ if ( dynamicRouteRule . test ( curPath ) ) {
320+ isDynamic = true ;
321+ excludePathname = curPath ;
322+ } else {
323+ isMatch = curPath === pathname ;
324+ }
325+ } else if ( curPath instanceof RegExp ) {
326+ isMatch = curPath . test ( pathname ) ;
327+ }
328+ // dynamic route
329+ if ( isDynamic ) {
330+ const matchFn = buildDynamicMatchFn ( excludePathname ) ;
331+ const ret = matchFn ( trimPathnameLeft ( pathname ) . split ( "/" ) ) ;
332+ isMatch = ret . matched ;
333+ if ( isMatch ) params = ret . params ;
236334 }
237335 if ( isMatch && item . method ) {
238336 return item . method === method ;
@@ -246,6 +344,7 @@ program
246344 matched : true ,
247345 pathname,
248346 exclude : true ,
347+ params,
249348 } ;
250349 }
251350 // not in exclude
@@ -381,35 +480,12 @@ program
381480 ? config . method
382481 : [ config . method ]
383482 : undefined ;
384- if ( / \/ : / . test ( key ) ) {
385- const segments = trimPathnameLeft ( key ) . split ( "/" ) ;
386- const pathSegs = [ ] ;
387- const patterns = [ ] ;
388- for ( const seg of segments ) {
389- if ( seg . startsWith ( ":" ) ) {
390- let len = seg . length ;
391- let optional = false ;
392- if ( seg . endsWith ( "?" ) ) {
393- optional = true ;
394- len -- ;
395- }
396- const param = seg . slice ( 1 , len ) ;
397- patterns . push ( {
398- param,
399- optional,
400- } ) ;
401- pathSegs . push ( `_${ param } ` ) ;
402- } else {
403- patterns . push ( {
404- path : seg ,
405- optional : false ,
406- } ) ;
407- pathSegs . push ( seg ) ;
408- }
409- }
483+ if ( dynamicRouteRule . test ( key ) ) {
484+ const { pathSegs, patterns } = getDynamicRoutePatterns ( key ) ;
410485 const pathname = hasOwn ( config , "rewrite" )
411486 ? trimPathnameLeft ( config . rewrite )
412487 : pathSegs . join ( "/" ) ;
488+ const matchFn = buildDynamicMatchFn ( key , patterns ) ;
413489 const matcher = ( segs , method ) => {
414490 // first, check the method if is matched
415491 if ( allowMethods && ! allowMethods . includes ( method ) ) {
@@ -418,39 +494,16 @@ program
418494 } ;
419495 }
420496 // check the pathname segments
421- const params = { } ;
422- for ( const [ index , seg ] of Object . entries ( segs ) ) {
423- const pattern = patterns [ index ] || { } ;
424- if ( hasOwn ( pattern , "path" ) ) {
425- if ( pattern . path === seg ) {
426- continue ;
427- }
428- } else if ( hasOwn ( pattern , "param" ) ) {
429- params [ pattern . param ] = seg ;
430- continue ;
431- }
497+ const ret = matchFn ( segs ) ;
498+ if ( ret . matched ) {
432499 return {
433- matched : false ,
434- params,
500+ matched : true ,
501+ params : ret . params ,
502+ pathname,
503+ origPathname : key ,
435504 } ;
436505 }
437- if ( patterns . length > segs . length ) {
438- const leavePatterns = patterns . slice ( segs . length ) ;
439- for ( const pattern of leavePatterns ) {
440- if ( ! pattern . optional ) {
441- return {
442- matched : false ,
443- params,
444- } ;
445- }
446- }
447- }
448- return {
449- matched : true ,
450- params,
451- pathname,
452- origPathname : key ,
453- } ;
506+ return ret ;
454507 } ;
455508 dynamicRouteMatcher . push ( matcher ) ;
456509 } else {
@@ -518,6 +571,7 @@ program
518571 let matched = true ;
519572 let searchExtensions = extensions ;
520573 let reason = "" ;
574+ let params = { } ;
521575 printDebugInfo ( "Request url" , addr ) ;
522576 // print the debug information
523577 if ( debug ) printDebugInfo ( "The request's pathname" , pathname ) ;
@@ -552,6 +606,7 @@ program
552606 const prefixData = matchPrefix ( pathname , method ) ;
553607 matched = prefixData . matched ;
554608 pathname = prefixData . pathname ;
609+ params = prefixData . params || params ;
555610 if ( ! matched )
556611 reason = `The url's pathname not match a prefix ${
557612 hasPrefixExclude
@@ -586,7 +641,6 @@ program
586641 // do with dynamic routes
587642 const pathSegs = pathname . split ( "/" ) ;
588643 const dynamicRoute = matchDynamicRoute ( pathSegs , method ) ;
589- let params = { } ;
590644 if ( dynamicRoute . matched ) {
591645 pathname = dynamicRoute . pathname ;
592646 params = dynamicRoute . params ;
0 commit comments