From db646d501982a5bca79bdc5da289d8422147c17e Mon Sep 17 00:00:00 2001 From: Craig Harshbarger Date: Sat, 6 Jan 2024 21:33:36 -0600 Subject: [PATCH] Implement router.route --- src/utilities/createRouter.browser.spec.ts | 45 ++++++++++++++++++++ src/utilities/createRouter.spec.ts | 6 +++ src/utilities/createRouter.ts | 49 +++++++++++++++++----- 3 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 src/utilities/createRouter.browser.spec.ts create mode 100644 src/utilities/createRouter.spec.ts diff --git a/src/utilities/createRouter.browser.spec.ts b/src/utilities/createRouter.browser.spec.ts new file mode 100644 index 00000000..039edcfa --- /dev/null +++ b/src/utilities/createRouter.browser.spec.ts @@ -0,0 +1,45 @@ +import { expect, test } from 'vitest' +import { createRouter } from '@/utilities/createRouter' +import { component } from '@/utilities/testHelpers' + +test('initial route is set', () => { + const root = { + name: 'root', + component, + path: '/', + } + + const { route } = createRouter([root], { + initialUrl: root.path, + }) + + expect(route.matched).toMatchObject(root) +}) + +test('throws error if route is not matched', () => { + expect(() => createRouter([])).toThrowError('not implemented') +}) + +test('updates the route when navigating', async () => { + const first = { + name: 'first', + component, + path: '/first', + } + + const second = { + name: 'second', + component, + path: '/second', + } + + const { push, route } = createRouter([first, second], { + initialUrl: first.path, + }) + + expect(route.matched).toMatchObject(first) + + await push(second.path) + + expect(route.matched).toMatchObject(second) +}) \ No newline at end of file diff --git a/src/utilities/createRouter.spec.ts b/src/utilities/createRouter.spec.ts new file mode 100644 index 00000000..fee23267 --- /dev/null +++ b/src/utilities/createRouter.spec.ts @@ -0,0 +1,6 @@ +import { expect, test } from 'vitest' +import { createRouter } from '@/utilities/createRouter' + +test('throws error if initial route is not set', () => { + expect(() => createRouter([])).toThrowError('initialUrl must be set if window.location is unavailable') +}) \ No newline at end of file diff --git a/src/utilities/createRouter.ts b/src/utilities/createRouter.ts index f26d7e4a..c27af752 100644 --- a/src/utilities/createRouter.ts +++ b/src/utilities/createRouter.ts @@ -1,8 +1,13 @@ -import { readonly } from 'vue' +import { DeepReadonly, reactive, readonly } from 'vue' import { Resolved, Route, RouteMethods, Routes } from '@/types' import { createRouteMethods, createRouterNavigation, resolveRoutes, routeMatch } from '@/utilities' +import { isBrowser } from '@/utilities/isBrowser' import { resolveRoutesRegex } from '@/utilities/resolveRoutesRegex' +type RouterOptions = { + initialUrl?: string, +} + type RouterPush = (url: string, options?: { replace: boolean }) => Promise type RouterReplace = (url: string) => Promise @@ -10,7 +15,7 @@ export type Router< TRoutes extends Routes > = { routes: RouteMethods, - route: Readonly>, + route: DeepReadonly>, push: RouterPush, replace: RouterReplace, back: () => void, @@ -18,24 +23,48 @@ export type Router< go: (delta: number) => void, } -export function createRouter(routes: T): Router { +export function createRouter(routes: T, options?: RouterOptions): Router { const resolved = resolveRoutes(routes) const resolvedWithRegex = resolveRoutesRegex(resolved) const navigation = createRouterNavigation({ onLocationUpdate, }) - // todo: implement this - const route: Router['route'] = readonly({} as any) + const route: Resolved = reactive(getInitialRoute()) - async function onLocationUpdate(url: string): Promise { - const match = routeMatch(resolvedWithRegex, url) + function getInitialUrl(): string { + if (options?.initialUrl) { + return options.initialUrl + } + + if (isBrowser()) { + return window.location.toString() + } + + throw new Error('initialUrl must be set if window.location is unavailable') + } - if (!match) { + function getInitialRoute(): Resolved { + const url = getInitialUrl() + + return getRoute(url) + } + + function getRoute(url: string): Resolved { + const route = routeMatch(resolvedWithRegex, url) + + if (!route) { + // not found throw 'not implemented' } - throw 'not implemented' + return route + } + + async function onLocationUpdate(url: string): Promise { + const newRoute = getRoute(url) + + Object.assign(route, newRoute) } const push: RouterPush = async (url, options) => { @@ -48,7 +77,7 @@ export function createRouter(routes: T): Router { const router = { routes: createRouteMethods(resolved), - route, + route: readonly(route), push, replace, forward: navigation.forward,