11import { stripSlashes } from '@feathersjs/commons' ;
2- import { BadRequest } from '@feathersjs/errors' ;
32
43export interface LookupData {
54 params : { [ key : string ] : string } ;
@@ -12,62 +11,74 @@ export interface LookupResult<T> extends LookupData {
1211export class RouteNode < T = any > {
1312 data ?: T ;
1413 children : { [ key : string ] : RouteNode } = { } ;
15- placeholder ? : RouteNode ;
14+ placeholders : RouteNode [ ] = [ ] ;
1615
17- constructor ( public name : string ) { }
16+ constructor ( public name : string , public depth : number ) { }
1817
1918 insert ( path : string [ ] , data : T ) : RouteNode < T > {
20- if ( path . length === 0 ) {
19+ if ( this . depth === path . length ) {
20+ if ( this . data !== undefined ) {
21+ throw new Error ( `Path ${ path . join ( '/' ) } already exists` ) ;
22+ }
23+
2124 this . data = data ;
2225 return this ;
2326 }
2427
25- const [ current , ...rest ] = path ;
28+ const current = path [ this . depth ] ;
29+ const nextDepth = this . depth + 1 ;
2630
2731 if ( current . startsWith ( ':' ) ) {
28- const { placeholder } = this ;
29- const name = current . substring ( 1 ) ;
32+ // Insert a placeholder node like /messages/:id
33+ const placeholderName = current . substring ( 1 ) ;
34+ let placeholder = this . placeholders . find ( p => p . name === placeholderName ) ;
3035
3136 if ( ! placeholder ) {
32- this . placeholder = new RouteNode ( name ) ;
33- } else if ( placeholder . name !== name ) {
34- throw new BadRequest ( `Can not add route with placeholder ':${ name } ' because placeholder ':${ placeholder . name } ' already exists` ) ;
37+ placeholder = new RouteNode ( placeholderName , nextDepth ) ;
38+ this . placeholders . push ( placeholder ) ;
3539 }
3640
37- return this . placeholder . insert ( rest , data ) ;
41+ return placeholder . insert ( path , data ) ;
3842 }
3943
40- this . children [ current ] = this . children [ current ] || new RouteNode ( current ) ;
44+ const child = this . children [ current ] || new RouteNode ( current , nextDepth ) ;
45+
46+ this . children [ current ] = child ;
4147
42- return this . children [ current ] . insert ( rest , data ) ;
48+ return child . insert ( path , data ) ;
4349 }
4450
4551 lookup ( path : string [ ] , info : LookupData ) : LookupResult < T > | null {
46- if ( path . length === 0 ) {
47- return {
52+ if ( path . length === this . depth ) {
53+ return this . data === undefined ? null : {
4854 ...info ,
4955 data : this . data
5056 }
5157 }
5258
53- const [ current , ... rest ] = path ;
59+ const current = path [ this . depth ] ;
5460 const child = this . children [ current ] ;
5561
5662 if ( child ) {
57- return child . lookup ( rest , info ) ;
63+ return child . lookup ( path , info ) ;
5864 }
5965
60- if ( this . placeholder ) {
61- info . params [ this . placeholder . name ] = current ;
62- return this . placeholder . lookup ( rest , info ) ;
66+ // This will return the first placeholder that matches early
67+ for ( const placeholder of this . placeholders ) {
68+ const result = placeholder . lookup ( path , info ) ;
69+
70+ if ( result !== null ) {
71+ result . params [ placeholder . name ] = current ;
72+ return result ;
73+ }
6374 }
6475
6576 return null ;
6677 }
6778}
6879
6980export class Router < T > {
70- root : RouteNode < T > = new RouteNode < T > ( '' ) ;
81+ constructor ( public root : RouteNode < T > = new RouteNode < T > ( '' , 0 ) ) { }
7182
7283 getPath ( path : string ) {
7384 return stripSlashes ( path ) . split ( '/' ) ;
0 commit comments