RouteRecord,
MatcherLocation,
MatcherLocationNormalized,
- MatcherLocationRedirect,
+ RouteRecordRedirect,
} from '../types'
import { NoRouteMatchError, InvalidRouteMatch } from '../errors'
import { createRouteRecordMatcher, RouteRecordMatcher } from './path-matcher'
resolve: (
location: Readonly<MatcherLocation>,
currentLocation: Readonly<MatcherLocationNormalized>
- ) => MatcherLocationNormalized | MatcherLocationRedirect
+ ) => MatcherLocationNormalized
}
const TRAILING_SLASH_RE = /(.)\/+$/
routes: RouteRecord[],
globalOptions: PathParserOptions
): RouterMatcher {
+ // normalized ordered array of matchers
const matchers: RouteRecordMatcher[] = []
function addRoute(
record: Readonly<RouteRecord>,
parent?: RouteRecordMatcher
): void {
- const mainNormalizedRecord: RouteRecordNormalized = normalizeRouteRecord(
- record
- )
+ const mainNormalizedRecord = normalizeRouteRecord(record)
const options: PathParserOptions = { ...globalOptions, ...record.options }
// TODO: can probably be removed now that we have our own parser and we handle this correctly
if (!options.strict)
mainNormalizedRecord.path = removeTrailingSlash(mainNormalizedRecord.path)
// generate an array of records to correctly handle aliases
- const normalizedRecords: RouteRecordNormalized[] = [mainNormalizedRecord]
+ const normalizedRecords: Array<
+ RouteRecordNormalized | RouteRecordRedirect
+ > = [mainNormalizedRecord]
if ('alias' in record && record.alias) {
const aliases =
typeof record.alias === 'string' ? [record.alias] : record.alias
function resolve(
location: Readonly<MatcherLocation>,
currentLocation: Readonly<MatcherLocationNormalized>
- ): MatcherLocationNormalized | MatcherLocationRedirect {
+ ): MatcherLocationNormalized {
let matcher: RouteRecordMatcher | undefined
let params: PathParams = {}
let path: MatcherLocationNormalized['path']
// params are automatically encoded
// TODO: try catch to provide better error messages
path = matcher.stringify(params)
-
- if ('redirect' in matcher.record) {
- const { redirect } = matcher.record
- return {
- redirect,
- normalizedLocation: {
- name,
- path,
- matched: [],
- params,
- meta: matcher.record.meta || {},
- },
- }
- }
} else if ('path' in location) {
matcher = matchers.find(m => m.re.test(location.path))
// matcher should have a value after the loop
path = location.path
name = matcher.record.name
- if ('redirect' in matcher.record) {
- const { redirect } = matcher.record
- return {
- redirect,
- normalizedLocation: {
- name,
- path,
- // TODO: verify this is good or add a comment
- matched: [],
- params,
- meta: matcher.record.meta || {},
- },
- }
- }
// location is a relative path
} else {
// match by name or path of current route
*/
export function normalizeRouteRecord(
record: Readonly<RouteRecord>
-): RouteRecordNormalized {
+): RouteRecordNormalized | RouteRecordRedirect {
if ('redirect' in record) {
- return {
- path: record.path,
- redirect: record.redirect,
- name: record.name,
- beforeEnter: record.beforeEnter,
- meta: record.meta,
- leaveGuards: [],
- }
+ // TODO: transform redirect into beforeEnter and remove type above
+ return record
} else {
return {
path: record.path,
TODO,
Immutable,
RouteParams,
- MatcherLocationNormalized,
} from './types'
import {
RouterHistory,
beforeEach(guard: NavigationGuard): ListenerRemover
afterEach(guard: PostNavigationGuard): ListenerRemover
- // TODO: also return a ListenerRemover
- onError(handler: ErrorHandler): void
+ onError(handler: ErrorHandler): ListenerRemover
isReady(): Promise<void>
install(app: App): void
const isClient = typeof window !== 'undefined'
-async function runGuardQueue(guards: Lazy<any>[]): Promise<void> {
- for (const guard of guards) {
- await guard()
- }
-}
-
-function extractChangingRecords(
- to: RouteLocationNormalized,
- from: RouteLocationNormalized
-) {
- const leavingRecords: RouteRecordMatched[] = []
- const updatingRecords: RouteRecordMatched[] = []
- const enteringRecords: RouteRecordMatched[] = []
-
- // TODO: could be optimized with one single for loop
- for (const record of from.matched) {
- if (to.matched.indexOf(record) < 0) leavingRecords.push(record)
- else updatingRecords.push(record)
- }
-
- for (const record of to.matched) {
- if (from.matched.indexOf(record) < 0) enteringRecords.push(record)
- }
-
- return [leavingRecords, updatingRecords, enteringRecords]
-}
-
export function createRouter({
history,
routes,
): RouteLocationNormalized {
// const objectLocation = routerLocationAsObject(location)
if (typeof location === 'string') {
- // TODO: remove as cast when redirect is removed from matcher
let locationNormalized = parseURL(parseQuery, location)
let matchedRoute = matcher.resolve(
{ path: locationNormalized.path },
currentLocation
- ) as MatcherLocationNormalized
+ )
return {
...locationNormalized,
// relative or named location, path is ignored
// for same reason TS thinks location.params can be undefined
- // TODO: remove cast like above
let matchedRoute = matcher.resolve(
hasParams
? // we know we have the params attribute
{ ...location, params: encodeParams((location as any).params) }
: location,
currentLocation
- ) as MatcherLocationNormalized
+ )
// put back the unencoded params as given by the user (avoid the cost of decoding them)
matchedRoute.params = hasParams
}
}
- // function oldresolveLocation(
- // location: MatcherLocation & Required<RouteQueryAndHash>,
- // currentLocation?: RouteLocationNormalized,
- // redirectedFrom?: RouteLocationNormalized
- // // ensure when returning that the redirectedFrom is a normalized location
- // ): RouteLocationNormalized {
- // currentLocation = currentLocation || currentRoute.value
- // // TODO: still return a normalized location with no matched records if no location is found
- // const matchedRoute = matcher.resolve(location, currentLocation)
-
- // if ('redirect' in matchedRoute) {
- // const { redirect } = matchedRoute
- // // target location normalized, used if we want to redirect again
- // const normalizedLocation: RouteLocationNormalized = {
- // ...matchedRoute.normalizedLocation,
- // ...normalizeLocation({
- // path: matchedRoute.normalizedLocation.path,
- // query: location.query,
- // hash: location.hash,
- // }),
- // redirectedFrom,
- // meta: {},
- // }
-
- // if (typeof redirect === 'string') {
- // // match the redirect instead
- // return resolveLocation(
- // normalizeLocation(redirect),
- // currentLocation,
- // normalizedLocation
- // )
- // } else if (typeof redirect === 'function') {
- // const newLocation = redirect(normalizedLocation)
-
- // if (typeof newLocation === 'string') {
- // return resolveLocation(
- // normalizeLocation(newLocation),
- // currentLocation,
- // normalizedLocation
- // )
- // }
-
- // // TODO: should we allow partial redirects? I think we should not because it's impredictable if
- // // there was a redirect before
- // // if (!('path' in newLocation) && !('name' in newLocation)) throw new Error('TODO: redirect canot be relative')
-
- // return resolveLocation(
- // {
- // ...newLocation,
- // query: newLocation.query || {},
- // hash: newLocation.hash || '',
- // },
- // currentLocation,
- // normalizedLocation
- // )
- // } else {
- // return resolveLocation(
- // {
- // ...redirect,
- // query: redirect.query || {},
- // hash: redirect.hash || '',
- // },
- // currentLocation,
- // normalizedLocation
- // )
- // }
- // } else {
- // // add the redirectedFrom field
- // const url = normalizeLocation({
- // path: matchedRoute.path,
- // query: location.query,
- // hash: location.hash,
- // })
- // return {
- // ...matchedRoute,
- // ...url,
- // redirectedFrom,
- // }
- // }
- // }
-
async function push(to: RouteLocation): Promise<RouteLocationNormalized> {
const toLocation: RouteLocationNormalized = (pendingLocation = resolve(to))
if (NavigationGuardRedirect.is(error)) {
// push was called while waiting in guards
if (pendingLocation !== toLocation) {
- // TODO: trigger onError as well
triggerError(new NavigationCancelled(toLocation, currentRoute.value))
}
// TODO: setup redirect stack
return push(error.to)
} else {
// TODO: write tests
- // triggerError as well
if (pendingLocation !== toLocation) {
// TODO: trigger onError as well
triggerError(new NavigationCancelled(toLocation, currentRoute.value))
app.provide('router', router)
app.provide('route', router.currentRoute)
}
+
+async function runGuardQueue(guards: Lazy<any>[]): Promise<void> {
+ for (const guard of guards) {
+ await guard()
+ }
+}
+
+function extractChangingRecords(
+ to: RouteLocationNormalized,
+ from: RouteLocationNormalized
+) {
+ const leavingRecords: RouteRecordMatched[] = []
+ const updatingRecords: RouteRecordMatched[] = []
+ const enteringRecords: RouteRecordMatched[] = []
+
+ // TODO: could be optimized with one single for loop
+ for (const record of from.matched) {
+ if (to.matched.indexOf(record) < 0) leavingRecords.push(record)
+ else updatingRecords.push(record)
+ }
+
+ for (const record of to.matched) {
+ if (from.matched.indexOf(record) < 0) enteringRecords.push(record)
+ }
+
+ return [leavingRecords, updatingRecords, enteringRecords]
+}