})
it('does not pass params as props by default', async () => {
- // TODO: rewrite so it is by default instead of explicitly stated
let noPropsWithParams = {
...routes.withParams,
- matched: [{ ...routes.withParams.matched[0], props: false }],
+ matched: [
+ {
+ components: { default: components.User },
+ instances: {},
+ path: '/users/:id',
+ },
+ ],
}
const { wrapper, route } = await factory(noPropsWithParams)
expect(wrapper.html()).toBe(`<div>User: default</div>`)
expect.stringMatching(/^#\/?$/)
)
})
-
- it.todo('warns if we provide a base with file://')
})
})
])
})
- // TODO: refactor trailing slash cases
- it.skip('trailing slash', () => {
- expect(tokenizePath('/foo/')).toEqual([
- [{ type: TokenType.Static, value: 'foo' }],
- [{ type: TokenType.Static, value: '' }],
- ])
- })
-
it('empty', () => {
expect(tokenizePath('')).toEqual([[]])
})
})
it('longer', () => {
expect(compare([[2]], [[3, 1]])).toEqual(1)
- // TODO: we are assuming we never pass end: false
+ // NOTE: we are assuming we never pass end: false
expect(compare([[3]], [[3, 1]])).toEqual(1)
expect(compare([[1, 3]], [[2]])).toEqual(1)
expect(compare([[4]], [[3]])).toEqual(-1)
const prop = propsOption[key]!
// @ts-ignore
if (!prop.required && prop.default)
- // TODO: function value
// @ts-ignore
copy[key] = prop.default
}
})
export function mount(
- // TODO: generic?
targetComponent: Parameters<typeof createApp>[0],
options: Partial<MountOptions> = {}
): Promise<Wrapper> {
}
}
- // TODO: how to cleanup?
const rootEl = document.createElement('div')
document.body.appendChild(rootEl)
activeWrapperRemovers.push(() => {
app.unmount(rootEl)
+ rootEl.remove()
})
})
}
// specify offset of the element
if (to.hash === '#anchor2') {
- // TODO: allow partial { y: 100 }
- position.offset = { x: 0, y: 100 }
+ position.offset = { y: 100 }
}
// bypass #1number check
<script>
import { defineComponent, toRefs, reactive, inject } from 'vue'
import { getData, delay } from '../api'
+import { onBeforeRouteUpdate } from '../../src'
export default defineComponent({
name: 'ComponentWithData',
async setup() {
- const data = reactive({ other: 'old' })
+ const data = reactive({ other: 'old', fromApi: null })
data.fromApi = await getData()
- // TODO: add sample with onBeforeRouteUpdate()
+ onBeforeRouteUpdate(async (to, from, next) => {
+ data.fromApi = await getData()
+ next()
+ })
return {
...toRefs(data),
},
beforeRouteUpdate(to, from, next) {
- // TODO: these do not work yet
- console.log('this', this)
+ console.log('in beforeRouteUpdate this', this)
next(vm => {
- console.log('in next', vm)
+ console.log('in next callback', vm)
})
},
})
isSameLocationObject(currentRoute.params, route.value.params)
)
- // TODO: handle replace prop
- // const method = unref(rep)
-
- function navigate(e: MouseEvent = {} as MouseEvent) {
- // TODO: handle navigate with empty parameters for scoped slot and composition api
- if (guardEvent(e)) router.push(unref(props.to))
+ function navigate(e: MouseEvent = {} as MouseEvent): Promise<any> {
+ if (guardEvent(e))
+ return router[unref(props.replace) ? 'replace' : 'push'](unref(props.to))
+ return Promise.resolve()
}
return {
*/
export default function createMemoryHistory(base: string = ''): RouterHistory {
let listeners: NavigationCallback[] = []
- // TODO: make sure this is right as the first location is nowhere so maybe this should be empty instead
let queue: HistoryLocationNormalized[] = [START]
let position: number = 0
const routerHistory: RouterHistory = {
// rewritten by Object.defineProperty
location: START,
- // TODO:
state: {},
base,
app.provide(routerKey, router)
app.provide(routeLocationKey, reactive(reactiveRoute))
- // TODO: merge strats for beforeRoute hooks
}
return matcherMap.get(name)
}
- // TODO: add routes to children of parent
function addRoute(
record: RouteRecordRaw,
parent?: RouteRecordMatcher,
while (parentMatcher) {
// reversed order so parents are at the beginning
- // TODO: check resolving child routes by path when parent has an alias
matched.unshift(parentMatcher.record)
parentMatcher = parentMatcher.parent
}
// passing originalRecord in Matcher.addRoute
if (!matcher.record.aliasOf === !parent.record.aliasOf)
parent.children.push(matcher)
- // else TODO: save alias children to be able to remove them
}
return matcher
} else if (char === ':') {
consumeBuffer()
state = TokenizerState.Param
- // } else if (char === '{') {
- // TODO: handle group (or drop it)
- // addCharToBuffer()
} else {
addCharToBuffer()
}
PostNavigationGuard,
START_LOCATION_NORMALIZED,
Lazy,
- TODO,
MatcherLocation,
RouteLocationNormalizedLoaded,
RouteLocation,
to: RouteLocationNormalized,
from: RouteLocationNormalizedLoaded,
savedPosition: Required<ScrollPositionCoordinates> | null
- ): // TODO: implement false nad refactor promise based type
- Awaitable<ScrollPosition | false | void>
+ ): Awaitable<ScrollPosition | false | void>
}
export interface RouterOptions {
function navigate(
to: RouteLocationNormalized,
from: RouteLocationNormalizedLoaded
- ): Promise<TODO> {
+ ): Promise<any> {
let guards: Lazy<any>[]
// all components here have been resolved once because we are leaving
- // TODO: refactor both together
guards = extractComponentsGuards(
from.matched.filter(record => to.matched.indexOf(record) < 0).reverse(),
'beforeRouteLeave',
return runGuardQueue(guards)
})
.then(() => {
- // TODO: at this point to.matched is normalized and does not contain any () => Promise<Component>
+ // NOTE: at this point to.matched is normalized and does not contain any () => Promise<Component>
// check in-component beforeRouteEnter
guards = extractComponentsGuards(
record.leaveGuards = []
// free the references
- // TODO: add tests
+ // TODO: to refactor once keep-alive and transition can be supported
record.instances = {}
}
// accept current navigation
currentRoute.value = toLocation
- // TODO: this doesn't work on first load. Moving it to RouterView could allow automatically handling transitions too maybe
- // TODO: refactor with a state getter
+ // TODO: call handleScroll in afterEach so it can also be triggered on
+ // duplicated navigation (e.g. same anchor navigation). It needs exposing
+ // the navigation information (type, direction)
if (isBrowser) {
const savedScroll = getSavedScrollPosition(
getScrollKey(toLocation.fullPath, 0)
pushWithRedirect(
(error as NavigationRedirectError).to,
toLocation
+ ).catch(() => {
// TODO: in dev show warning, in prod noop, same as initial navigation
- )
+ })
// avoid the then branch
return Promise.reject()
}
failure
)
})
- .catch(() => {})
+ .catch(() => {
+ // TODO: same as above: in dev show warning, in prod noop, same as initial navigation
+ })
})
// Initialization and Errors
import { Ref, ComputedRef, ComponentOptions } from 'vue'
import { RouteRecord, RouteRecordNormalized } from '../matcher/types'
import { HistoryState } from '../history/common'
+import { NavigationFailure } from '../errors'
export type Lazy<T> = () => Promise<T>
export type Override<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U
hash: string
name: RouteRecordName | null | undefined
params: RouteParams
- // TODO: make it an array?
redirectedFrom: RouteLocation | undefined
meta: Record<string | number | symbol, any>
}
'name' | 'path' | 'params' | 'matched' | 'meta'
> {}
-// TODO: remove any to type vm and use a generic that comes from the component
-// where the navigation guard callback is defined
export interface NavigationGuardCallback {
(): void
(error: Error): void
to: RouteLocationNormalized,
from: RouteLocationNormalized,
// TODO: move these types to a different file
- failure?: TODO
+ failure?: NavigationFailure | void
): any
}