@@ -33,12 +33,12 @@ export function compileRouter<
3333) => O [ "matchAll" ] extends true
3434 ? MatchedRoute < T > [ ]
3535 : MatchedRoute < T > | undefined {
36- const deps : any [ ] = [ ] ;
37- const compiled = compileRouteMatch ( router , deps , opts ) ;
36+ const ctx : CompilerContext = { opts : opts || { } , router , deps : [ ] } ;
37+ const compiled = compileRouteMatch ( ctx ) ;
3838 return new Function (
39- ...deps . map ( ( _ , i ) => "d" + ( i + 1 ) ) ,
39+ ...ctx . deps ! . map ( ( _ , i ) => "d" + ( i + 1 ) ) ,
4040 `return(m,p)=>{${ compiled } }` ,
41- ) ( ...deps ) ;
41+ ) ( ...ctx . deps ! ) ;
4242}
4343
4444/**
@@ -59,191 +59,173 @@ export function compileRouterToString(
5959 functionName ?: string ,
6060 opts ?: RouterCompilerOptions ,
6161) : string {
62- const compiled = `(m,p)=>{${ compileRouteMatch ( router , undefined , opts ) } }` ;
62+ const ctx : CompilerContext = { opts : opts || { } , router, deps : undefined } ;
63+ const compiled = `(m,p)=>{${ compileRouteMatch ( ctx ) } }` ;
6364 return functionName ? `const ${ functionName } =${ compiled } ;` : compiled ;
6465}
6566
6667// ------- internal functions -------
6768
68- // p: path
69- // s: path parts
70- // l: path parts length
71- // m: method
72- // r: matchAll matches
69+ interface CompilerContext {
70+ opts : RouterCompilerOptions ;
71+ router : RouterContext < any > ;
72+ deps : string [ ] | undefined ;
73+ }
7374
74- /**
75- * Compile a router to pattern matching statements
76- * @param router
77- * @param deps - Dependencies of the function scope
78- */
79- function compileRouteMatch (
80- router : RouterContext < any > ,
81- deps ?: any [ ] ,
82- opts ?: RouterCompilerOptions ,
83- ) : string {
84- let str = "" ;
75+ function compileRouteMatch ( ctx : CompilerContext ) : string {
76+ let code = "" ;
8577 const staticNodes = new Set < Node > ( ) ;
8678
87- for ( const key in router . static ) {
88- const node = router . static [ key ] ;
79+ for ( const key in ctx . router . static ) {
80+ const node = ctx . router . static [ key ] ;
8981 if ( node ?. methods ) {
9082 staticNodes . add ( node ) ;
91- str += `if(p===${ JSON . stringify ( key . replace ( / \/ $ / , "" ) || "/" ) } ){${ compileMethodMatch ( node . methods , [ ] , deps , - 1 , opts ) } }` ;
83+ code += `if(p===${ JSON . stringify ( key . replace ( / \/ $ / , "" ) || "/" ) } ){${ compileMethodMatch ( ctx , node . methods , [ ] , - 1 ) } }` ;
9284 }
9385 }
9486
95- const match = compileNode ( router . root , [ ] , 0 , deps , false , staticNodes , opts ) ;
87+ const match = compileNode ( ctx , ctx . router . root , [ ] , 0 , staticNodes ) ;
9688 if ( match ) {
97- str += " let s=p.split('/' ),l=s.length-1;" + match ;
89+ code += ` let s=p.split("/" ),l=s.length-1;${ match } ` ;
9890 }
9991
100- if ( ! str ) {
101- return opts ?. matchAll ? " return [];" : "" ;
92+ if ( ! code ) {
93+ return ctx . opts ?. matchAll ? ` return [];` : "" ;
10294 }
10395
104- return `${ opts ?. matchAll ? `let r=[];` : "" } if(p.charCodeAt(p.length-1)===47)p=p.slice(0,-1)||'/' ;${ str } ${ opts ?. matchAll ? "return r;" : "" } ` ;
96+ return `${ ctx . opts ?. matchAll ? `let r=[];` : "" } if(p.charCodeAt(p.length-1)===47)p=p.slice(0,-1)||"/" ;${ code } ${ ctx . opts ?. matchAll ? "return r;" : "" } ` ;
10597}
10698
10799function compileMethodMatch (
100+ ctx : CompilerContext ,
108101 methods : Record < string , MethodData < any > [ ] | undefined > ,
109102 params : string [ ] ,
110- deps : any [ ] | undefined ,
111103 currentIdx : number , // Set to -1 for non-param node
112- opts ?: RouterCompilerOptions ,
113104) : string {
114- let str = "" ;
105+ let code = "" ;
115106 for ( const key in methods ) {
116107 const data = methods [ key ] ;
117108 if ( data && data ?. length > 0 ) {
118109 // Don't check for matchAll method handler
119- if ( key !== "" ) str += `if(m==='${ key } ')` ;
120-
121- const dataValue = data [ 0 ] . data ;
122- let serializedData : string ;
123- if ( deps ) {
124- serializedData = `d${ deps . push ( dataValue ) } ` ;
125- } else if ( opts ?. serialize ) {
126- serializedData = opts . serialize ( dataValue ) ;
127- } else if ( typeof dataValue ?. toJSON === "function" ) {
128- serializedData = dataValue . toJSON ( ) ;
129- } else {
130- serializedData = JSON . stringify ( dataValue ) ;
131- }
132- let res = `{data:${ serializedData } ` ;
133-
134- // Add param properties
135- const { paramsMap } = data [ 0 ] ;
136- if ( paramsMap && paramsMap . length > 0 ) {
137- // Check for optional end parameters
138- const required =
139- ! paramsMap [ paramsMap . length - 1 ] [ 2 ] && currentIdx !== - 1 ;
140- if ( required ) str += `if(l>=${ currentIdx } )` ;
141-
142- // Create the param object based on previous parameters
143- res += ",params:{" ;
144- for ( let i = 0 ; i < paramsMap . length ; i ++ ) {
145- const map = paramsMap [ i ] ;
146-
147- res +=
148- typeof map [ 1 ] === "string"
149- ? `${ JSON . stringify ( map [ 1 ] ) } :${ params [ i ] } ,`
150- : `...(${ map [ 1 ] . toString ( ) } .exec(${ params [ i ] } ))?.groups,` ;
151- }
152- res += "}" ;
153- }
110+ if ( key !== "" ) code += `if(m==="${ key } ")` ;
111+ code += compileFinalMatch ( ctx , data [ 0 ] , currentIdx , params ) ;
112+ }
113+ }
114+ return code ;
115+ }
154116
155- str += opts ?. matchAll ? `r.unshift(${ res } });` : `return ${ res } };` ;
117+ function compileFinalMatch (
118+ ctx : CompilerContext ,
119+ data : MethodData < any > ,
120+ currentIdx : number ,
121+ params : string [ ] ,
122+ ) : string {
123+ let code = "" ;
124+ let ret = `{data:${ serializeData ( ctx , data . data ) } ` ;
125+
126+ // Add param properties
127+ const { paramsMap } = data ;
128+ if ( paramsMap && paramsMap . length > 0 ) {
129+ // Check for optional end parameters
130+ const required = ! paramsMap [ paramsMap . length - 1 ] [ 2 ] && currentIdx !== - 1 ;
131+ if ( required ) code += `if(l>=${ currentIdx } )` ;
132+ // Create the param object based on previous parameters
133+ ret += ",params:{" ;
134+ for ( let i = 0 ; i < paramsMap . length ; i ++ ) {
135+ const map = paramsMap [ i ] ;
136+ ret +=
137+ typeof map [ 1 ] === "string"
138+ ? `${ JSON . stringify ( map [ 1 ] ) } :${ params [ i ] } ,`
139+ : `...(${ map [ 1 ] . toString ( ) } .exec(${ params [ i ] } ))?.groups,` ;
156140 }
141+ ret += "}" ;
157142 }
158- return str ;
143+ return (
144+ code + ( ctx . opts ?. matchAll ? `r.unshift(${ ret } });` : `return ${ ret } };` )
145+ ) ;
159146}
160147
161- /**
162- * Compile a node to matcher logic
163- */
164148function compileNode (
149+ ctx : CompilerContext ,
165150 node : Node < any > ,
166151 params : string [ ] ,
167152 startIdx : number ,
168- deps : any [ ] | undefined ,
169- isParamNode : boolean ,
170153 staticNodes : Set < Node > ,
171- opts ?: RouterCompilerOptions ,
172154) : string {
173- let str = "" ;
155+ let code = "" ;
174156
175157 if ( node . methods && ! staticNodes . has ( node ) ) {
176158 const match = compileMethodMatch (
159+ ctx ,
177160 node . methods ,
178161 params ,
179- deps ,
180- isParamNode ? startIdx : - 1 ,
181- opts ,
162+ node . key === "*" ? startIdx : - 1 ,
182163 ) ;
183164 if ( match ) {
184- str += `if(l===${ startIdx } ${ isParamNode ? `||l===${ startIdx - 1 } ` : "" } ){${ match } }` ;
165+ const hasLastOptionalParam = node . key === "*" ;
166+ code += `if(l===${ startIdx } ${ hasLastOptionalParam ? `||l===${ startIdx - 1 } ` : "" } ){${ match } }` ;
185167 }
186168 }
187169
188170 if ( node . static ) {
189171 for ( const key in node . static ) {
190172 const match = compileNode (
173+ ctx ,
191174 node . static [ key ] ,
192175 params ,
193176 startIdx + 1 ,
194- deps ,
195- false ,
196177 staticNodes ,
197- opts ,
198178 ) ;
199179 if ( match ) {
200- str += `if(s[${ startIdx + 1 } ]===${ JSON . stringify ( key ) } ){${ match } }` ;
180+ code += `if(s[${ startIdx + 1 } ]===${ JSON . stringify ( key ) } ){${ match } }` ;
201181 }
202182 }
203183 }
204184
205185 if ( node . param ) {
206186 const match = compileNode (
187+ ctx ,
207188 node . param ,
208189 [ ...params , `s[${ startIdx + 1 } ]` ] ,
209190 startIdx + 1 ,
210- deps ,
211- true ,
212191 staticNodes ,
213- opts ,
214192 ) ;
215193 if ( match ) {
216- str += match ;
194+ code += match ;
217195 }
218196 }
219197
220198 if ( node . wildcard ) {
221199 const { wildcard } = node ;
222- if ( hasChild ( wildcard ) ) {
200+ if ( wildcard . static || wildcard . param || wildcard . wildcard ) {
223201 throw new Error ( "Compiler mode does not support patterns after wildcard" ) ;
224202 }
225203
226204 if ( wildcard . methods ) {
227205 const match = compileMethodMatch (
206+ ctx ,
228207 wildcard . methods ,
229208 [ ...params , `s.slice(${ startIdx + 1 } ).join('/')` ] ,
230- deps ,
231209 startIdx ,
232- opts ,
233210 ) ;
234211 if ( match ) {
235- str += match ;
212+ code += match ;
236213 }
237214 }
238215 }
239216
240- return str ;
217+ return code ;
241218}
242219
243- /**
244- * Whether the current node has children nodes
245- * @param n
246- */
247- function hasChild ( n : Node < any > ) : boolean {
248- return ! ! ( n . static || n . param || n . wildcard ) ;
220+ function serializeData ( ctx : CompilerContext , value : any ) : string {
221+ if ( ctx . deps ) {
222+ return `d${ ctx . deps . push ( value ) } ` ;
223+ }
224+ if ( ctx . opts ?. serialize ) {
225+ return ctx . opts . serialize ( value ) ;
226+ }
227+ if ( typeof value ?. toJSON === "function" ) {
228+ return value . toJSON ( ) ;
229+ }
230+ return JSON . stringify ( value ) ;
249231}
0 commit comments