])
})
+ // 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('static single', () => {
matchRegExp('^/?$', [[]], { strict: false })
- })
-
- it('empty path', () => {
- matchRegExp('^$', [[]], { strict: true })
+ matchRegExp('^/$', [[]], { strict: true })
})
it('strict /', () => {
})
it('makes the difference between "" and "/" when strict', () => {
- matchParams('', '/', null, { strict: true })
- matchParams('/', '', null, { strict: true })
+ matchParams('/foo', '/foo/', null, { strict: true })
+ matchParams('/foo/', '/foo', null, { strict: true })
})
- it('allow a trailing slash', () => {
+ it('allows a trailing slash', () => {
matchParams('/home', '/home/', {})
matchParams('/a/b', '/a/b/', {})
})
+ it('enforces a trailing slash', () => {
+ matchParams('/home/', '/home', null, { strict: true })
+ })
+
it('allow a trailing slash in repeated params', () => {
matchParams('/a/:id+', '/a/b/c/d/', { id: ['b', 'c', 'd'] })
matchParams('/a/:id*', '/a/b/c/d/', { id: ['b', 'c', 'd'] })
},
rootProps = {}
) {
+ // TODO: update with alpha-4
const app = createApp()
app.provide('router', router)
+ app.provide('route', router.currentRoute)
const { template, components, ...ComponentWithoutTemplate } = Component
- // @ts-ignore
+ // @ts-ignore TODO: remove?
ComponentWithoutTemplate.components = {}
for (const componentName in components) {
app.component(componentName, components[componentName])
<a href="/documents/€">/documents/€ (force reload): not valid tho</a>
</li>
<li>
- <router-link to="/">/</router-link>
+ <router-link to="/">Home</router-link>
+ </li>
+ <li>
+ <router-link to="/nested/nested/nested"
+ >/nested/nested/nested</router-link
+ >
</li>
<li>
<router-link to="/long-0">/long-0</router-link>
<router-link to="/with-data">/with-data</router-link>
</li>
</ul>
- <transition
+ <!-- <transition
name="fade"
mode="out-in"
@before-enter="flushWaiter"
@before-leave="setupWaiter"
- >
- <router-view></router-view>
- </transition>
+ > -->
+ <router-view></router-view>
+ <!-- </transition> -->
</div>
</template>
import { createRouter, createHistory } from '../src'
import Home from './views/Home.vue'
+import Nested from './views/Nested.vue'
import User from './views/User.vue'
import NotFound from './views/NotFound.vue'
import component from './views/Generic.vue'
{ path: '/with-data', component: ComponentWithData, name: 'WithData' },
{ path: '/rep/:a*', component: component, name: 'repeat' },
{ path: '/:data(.*)', component: NotFound, name: 'NotFound' },
+ {
+ path: '/nested',
+ component: Nested,
+ name: 'Nested',
+ children: [
+ {
+ path: 'nested',
+ component: Nested,
+ children: [{ path: 'nested', component: Nested }],
+ },
+ ],
+ },
],
async scrollBehavior(to, from, savedPosition) {
await scrollWaiter.wait()
<template>
- <div>Home</div>
+ <div>
+ <div>Home</div>
+ <p>My Data is: {{ someData }}</p>
+ toggle: {{ log(toggle) }}
+ </div>
</template>
<script>
-import { defineComponent } from 'vue'
+import { defineComponent, getCurrentInstance, inject, ref } from 'vue'
+// import { guardSymbol } from '../../src/components/View'
export default defineComponent({
name: 'Home',
+ data: () => ({
+ toggle: false,
+ }),
+
+ setup() {
+ const me = getCurrentInstance()
+ // const registerGuard = inject(guardSymbol)
+ // console.log('calling setup in Home')
+ // await registerGuard(me)
+ // emit('registerGuard', getCurrentInstance())
+
+ function log(value) {
+ console.log(value)
+ return value
+ }
+
+ return {
+ log,
+ someData: ref(0),
+ }
+ },
+
+ _beforeRouteEnter() {
+ this.toggle = true
+ },
})
</script>
--- /dev/null
+<template>
+ <div>
+ <p>Nested level {{ level }}</p>
+ <router-view v-if="level < 6"></router-view>
+ </div>
+</template>
+
+<script>
+import { defineComponent, inject, provide } from 'vue'
+// import { guardSymbol } from '../../src/components/View'
+
+export default defineComponent({
+ name: 'Nested',
+ setup() {
+ const level = inject('level', 1)
+ provide('level', level + 1)
+ // const registerGuard = inject(guardSymbol)
+ // await registerGuard()
+ // console.log('done waiting')
+
+ return {
+ level,
+ }
+ },
+})
+</script>
name: 'User',
setup() {
const route = inject('route')
+ console.log('calling setup in User')
return { route }
},
})
},
setup(props, { attrs }) {
- const router = inject('router')
+ const route = inject('route')
const depth: number = inject('routerViewDepth', 0)
provide('routerViewDepth', depth + 1)
- const ViewComponent = computed<Component | void>(() => {
- const matched = router.currentRoute.value.matched[depth]
+ const ViewComponent = computed<Component | undefined>(() => {
+ const matched = route.value.matched[depth]
- if (!matched) return null
-
- return matched.components[props.name]
+ return matched && matched.components[props.name]
})
- return () =>
- ViewComponent.value ? h(ViewComponent.value as any, attrs) : []
+ return () => {
+ return ViewComponent.value
+ ? h(ViewComponent.value as any, { ...attrs })
+ : null
+ }
},
})
import { createRouter, Router } from './router'
-import { App } from '@vue/runtime-core'
+import { App, Ref } from '@vue/runtime-core'
import createHistory from './history/html5'
import createMemoryHistory from './history/memory'
import createHashHistory from './history/hash'
declare module '@vue/runtime-core' {
function inject(name: 'router'): Router
- function inject(name: 'route'): RouteLocationNormalized
- function inject(name: 'routerViewDepth'): number
+ function inject(name: 'route'): Ref<RouteLocationNormalized>
}
// @ts-ignore: we are not importing it so it complains
app.mixin({
beforeCreate() {
if (!started) {
- router.setActiveApp(this)
-
// TODO: this initial navigation is only necessary on client, on server it doesn't make sense
// because it will create an extra unecessary navigation and could lead to problems
router.push(router.history.location).catch(err => {
// the root segment needs special treatment
const segmentScores: number[] = segment.length ? [] : [PathScore.Root]
+ // allow trailing slash
+ if (options.strict && !segment.length) pattern += '/'
for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
const token = segment[tokenIndex]
// resets the score if we are inside a sub segment /:a-other-:b
} else if (char === ':') {
consumeBuffer()
state = TokenizerState.Param
- } else if (char === '{') {
+ // } else if (char === '{') {
// TODO: handle group (or drop it)
- addCharToBuffer()
+ // addCharToBuffer()
} else {
addCharToBuffer()
}
import { encodeParam } from './utils/encoding'
import { decode } from './utils/encoding'
import { ref, Ref, markNonReactive } from '@vue/reactivity'
+import { nextTick } from '@vue/runtime-core'
type ErrorHandler = (error: any) => any
// resolve, reject arguments of Promise constructor
push(to: RouteLocation): Promise<RouteLocationNormalized>
replace(to: RouteLocation): Promise<RouteLocationNormalized>
- // TODO: find a way to remove it
- setActiveApp(vm: TODO): void
-
beforeEach(guard: NavigationGuard): ListenerRemover
afterEach(guard: PostNavigationGuard): ListenerRemover
let onReadyCbs: OnReadyCallback[] = []
// TODO: should these be triggered before or after route.push().catch()
let errorHandlers: ErrorHandler[] = []
- let app: TODO
let ready: boolean = false
function resolve(
) {
if (!scrollBehavior) return
- await app.$nextTick()
+ await nextTick()
const position = await scrollBehavior(to, from, scrollPosition || null)
console.log('scrolling to', position)
scrollToPosition(position)
}
- function setActiveApp(vm: TODO) {
- app = vm
- }
-
const router: Router = {
currentRoute,
push,
isReady,
history,
- setActiveApp,
}
return router