@@ -17,22 +17,33 @@ export type RouteModule = {
1717 readonly asyncDeps ?: DependencyDescriptor [ ]
1818}
1919
20+ export type RoutingOptions = {
21+ routes ?: Route [ ]
22+ rewrites ?: Record < string , string >
23+ baseUrl ?: string
24+ defaultLocale ?: string
25+ locales ?: string [ ]
26+ }
27+
2028export class Routing {
21- private _routes : Route [ ]
2229 private _baseUrl : string
2330 private _defaultLocale : string
2431 private _locales : string [ ]
32+ private _routes : Route [ ]
33+ private _rewrites : Record < string , string >
2534
26- constructor (
27- routes : Route [ ] = [ ] ,
28- baseUrl : string = '/ ' ,
29- defaultLocale : string = 'en' ,
30- locales : string [ ] = [ ]
31- ) {
32- this . _routes = routes
35+ constructor ( {
36+ baseUrl = '/' ,
37+ defaultLocale = 'en ' ,
38+ locales = [ ] ,
39+ routes = [ ] ,
40+ rewrites = { } ,
41+ } : RoutingOptions ) {
3342 this . _baseUrl = baseUrl
3443 this . _defaultLocale = defaultLocale
3544 this . _locales = locales
45+ this . _routes = routes
46+ this . _rewrites = rewrites
3647 }
3748
3849 get baseUrl ( ) {
@@ -107,10 +118,10 @@ export class Routing {
107118
108119 createRouter ( location ?: { pathname : string , search ?: string } ) : [ RouterURL , RouteModule [ ] ] {
109120 const loc = location || ( window as any ) . location || { pathname : '/' }
110- const query = new URLSearchParams ( loc . search )
121+ const url = rewriteURL ( loc . pathname + ( loc . search || '' ) , this . _baseUrl , this . _rewrites )
111122
112123 let locale = this . _defaultLocale
113- let pathname = util . cleanPath ( util . trimPrefix ( loc . pathname , this . _baseUrl ) )
124+ let pathname = decodeURI ( url . pathname )
114125 let pagePath = ''
115126 let params : Record < string , string > = { }
116127 let chain : RouteModule [ ] = [ ]
@@ -139,7 +150,7 @@ export class Routing {
139150 }
140151 } , true )
141152
142- return [ { locale, pathname, pagePath, params, query } , chain ]
153+ return [ { locale, pathname, pagePath, params, query : url . searchParams } , chain ]
143154 }
144155
145156 lookup ( callback : ( path : Route [ ] ) => Boolean | void ) {
@@ -149,20 +160,20 @@ export class Routing {
149160 private _lookup (
150161 callback : ( path : Route [ ] ) => Boolean | void ,
151162 skipNestIndex = false ,
152- __tracing : Route [ ] = [ ] ,
153- __routes = this . _routes
163+ _tracing : Route [ ] = [ ] ,
164+ _routes = this . _routes
154165 ) {
155- for ( const route of __routes ) {
156- if ( skipNestIndex && __tracing . length > 0 && route . path === '/' ) {
166+ for ( const route of _routes ) {
167+ if ( skipNestIndex && _tracing . length > 0 && route . path === '/' ) {
157168 continue
158169 }
159- if ( callback ( [ ...__tracing , route ] ) === false ) {
170+ if ( callback ( [ ..._tracing , route ] ) === false ) {
160171 return false
161172 }
162173 }
163- for ( const route of __routes ) {
174+ for ( const route of _routes ) {
164175 if ( route . path !== '/' && route . children ?. length ) {
165- if ( this . _lookup ( callback , skipNestIndex , [ ...__tracing , route ] , route . children ) === false ) {
176+ if ( this . _lookup ( callback , skipNestIndex , [ ..._tracing , route ] , route . children ) === false ) {
166177 return false
167178 }
168179 }
@@ -201,14 +212,41 @@ function matchPath(routePath: string, locPath: string): [Record<string, string>,
201212 return [ params , true ]
202213}
203214
215+ /** `rewriteURL` returns a rewrited URL */
216+ export function rewriteURL ( reqUrl : string , baseUrl : string , rewrites : Record < string , string > ) : URL {
217+ const url = new URL ( 'http://localhost' + reqUrl )
218+ if ( baseUrl !== '/' ) {
219+ url . pathname = util . trimPrefix ( decodeURI ( url . pathname ) , baseUrl )
220+ }
221+ for ( const path in rewrites ) {
222+ const to = rewrites [ path ]
223+ const [ params , ok ] = matchPath ( path , decodeURI ( url . pathname ) )
224+ if ( ok ) {
225+ const { searchParams } = url
226+ url . href = 'http://localhost' + util . cleanPath ( to . replace ( / : ( .+ ) ( \/ | & | $ ) / g, ( s , k , e ) => {
227+ if ( k in params ) {
228+ return params [ k ] + e
229+ }
230+ return s
231+ } ) )
232+ for ( const [ key , value ] of url . searchParams . entries ( ) ) {
233+ searchParams . append ( key , value )
234+ }
235+ url . search = searchParams . toString ( )
236+ break
237+ }
238+ }
239+ return url
240+ }
241+
204242export async function redirect ( url : string , replace ?: boolean ) {
205243 const { location, history } = window as any
206244
207245 if ( ! util . isNEString ( url ) ) {
208246 return
209247 }
210248
211- if ( isHttpUrl ( url ) ) {
249+ if ( util . isLikelyHttpURL ( url ) || url . startsWith ( 'file://' ) || url . startsWith ( 'mailto:' ) ) {
212250 location . href = url
213251 return
214252 }
@@ -222,15 +260,6 @@ export async function redirect(url: string, replace?: boolean) {
222260 events . emit ( 'popstate' , { type : 'popstate' , resetScroll : true } )
223261}
224262
225- export function isHttpUrl ( url : string ) {
226- try {
227- const { protocol } = new URL ( url )
228- return protocol === 'https:' || protocol === 'http:'
229- } catch ( error ) {
230- return false
231- }
232- }
233-
234263export function isModuleURL ( url : string ) {
235264 for ( const ext of moduleExts ) {
236265 if ( url . endsWith ( '.' + ext ) ) {
0 commit comments