@@ -619,40 +619,65 @@ function negate(delimiter: string, backtrack: string) {
619
619
}
620
620
621
621
/**
622
- * Stringify token data into a path string.
622
+ * Stringify an array of tokens into a path string.
623
623
*/
624
- export function stringify ( data : TokenData ) {
625
- return data . tokens
626
- . map ( function stringifyToken ( token , index , tokens ) : string {
627
- if ( token . type === "text" ) return escapeText ( token . value ) ;
628
- if ( token . type === "group" ) {
629
- return `{${ token . tokens . map ( stringifyToken ) . join ( "" ) } }` ;
630
- }
624
+ function stringifyTokens ( tokens : Token [ ] ) : string {
625
+ let value = "" ;
626
+ let i = 0 ;
627
+
628
+ function name ( value : string ) {
629
+ const isSafe = isNameSafe ( value ) && isNextNameSafe ( tokens [ i ] ) ;
630
+ return isSafe ? value : JSON . stringify ( value ) ;
631
+ }
632
+
633
+ while ( i < tokens . length ) {
634
+ const token = tokens [ i ++ ] ;
631
635
632
- const isSafe =
633
- isNameSafe ( token . name ) && isNextNameSafe ( tokens [ index + 1 ] ) ;
634
- const key = isSafe ? token . name : JSON . stringify ( token . name ) ;
636
+ if ( token . type === "text" ) {
637
+ value += escapeText ( token . value ) ;
638
+ continue ;
639
+ }
635
640
636
- if ( token . type === "param" ) return `:${ key } ` ;
637
- if ( token . type === "wildcard" ) return `*${ key } ` ;
638
- throw new TypeError ( `Unexpected token: ${ token } ` ) ;
639
- } )
640
- . join ( "" ) ;
641
+ if ( token . type === "group" ) {
642
+ value += `{${ stringifyTokens ( token . tokens ) } }` ;
643
+ continue ;
644
+ }
645
+
646
+ if ( token . type === "param" ) {
647
+ value += `:${ name ( token . name ) } ` ;
648
+ continue ;
649
+ }
650
+
651
+ if ( token . type === "wildcard" ) {
652
+ value += `*${ name ( token . name ) } ` ;
653
+ continue ;
654
+ }
655
+
656
+ throw new TypeError ( `Unknown token type: ${ ( token as any ) . type } ` ) ;
657
+ }
658
+
659
+ return value ;
660
+ }
661
+
662
+ /**
663
+ * Stringify token data into a path string.
664
+ */
665
+ export function stringify ( data : TokenData ) {
666
+ return stringifyTokens ( data . tokens ) ;
641
667
}
642
668
643
669
/**
644
670
* Validate the parameter name contains valid ID characters.
645
671
*/
646
672
function isNameSafe ( name : string ) {
647
673
const [ first , ...rest ] = name ;
648
- if ( ! ID_START . test ( first ) ) return false ;
649
- return rest . every ( ( char ) => ID_CONTINUE . test ( char ) ) ;
674
+ return ID_START . test ( first ) && rest . every ( ( char ) => ID_CONTINUE . test ( char ) ) ;
650
675
}
651
676
652
677
/**
653
678
* Validate the next token does not interfere with the current param name.
654
679
*/
655
680
function isNextNameSafe ( token : Token | undefined ) {
656
- if ( ! token || token . type !== "text" ) return true ;
657
- return ! ID_CONTINUE . test ( token . value [ 0 ] ) ;
681
+ if ( token && token . type === "text" ) return ! ID_CONTINUE . test ( token . value [ 0 ] ) ;
682
+ return true ;
658
683
}
0 commit comments