From 909c45be199fbf48e55f3f7e681e93c259e82a14 Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Thu, 11 May 2023 12:38:51 +0200 Subject: [PATCH] feat: allow inject within global navigation guards --- .../guards/navigatioGuardsInjections.spec.ts | 51 +++++++++++++++++++ packages/router/src/router.ts | 28 +++++++--- 2 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts diff --git a/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts b/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts new file mode 100644 index 000000000..a660b1c12 --- /dev/null +++ b/packages/router/__tests__/guards/navigatioGuardsInjections.spec.ts @@ -0,0 +1,51 @@ +/** + * @jest-environment jsdom + */ +import { createDom, newRouter as createRouter } from '../utils' +import { mount } from '@vue/test-utils' +import { inject } from 'vue' +import { mockWarn } from 'jest-mock-warn' +import type { Router } from '../../src' + +describe('inject() within navigation guards', () => { + mockWarn() + beforeAll(() => { + createDom() + }) + + const PageComponent = { + template: `
Page
`, + } + + function factory(router: Router) { + return mount( + { + template: ``, + }, + { + global: { + plugins: [router], + provide: { + test: 'hello', + }, + }, + } + ) + } + + const globalGuards = ['beforeEach', 'beforeResolve', 'afterEach'] as const + + for (const guardName of globalGuards) { + it(`router.${guardName}()`, async () => { + expect.assertions(1) + const router = createRouter({ + routes: [{ path: '/', component: PageComponent }], + }) + router[guardName](() => { + expect(inject('test')).toBe('hello') + }) + factory(router) + await router.isReady() + }) + } +}) diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts index 8493484be..d2605a70b 100644 --- a/packages/router/src/router.ts +++ b/packages/router/src/router.ts @@ -13,6 +13,7 @@ import { RouteLocationOptions, MatcherLocationRaw, RouteParams, + NavigationGuardReturn, } from './types' import { RouterHistory, HistoryState, NavigationType } from './history/common' import { @@ -778,6 +779,14 @@ export function createRouter(options: RouterOptions): Router { return error ? Promise.reject(error) : Promise.resolve() } + function runWithContext(fn: () => T): T { + const app: App | undefined = installedApps.values().next().value + // support Vue < 3.3 + return app && typeof app.runWithContext === 'function' + ? app.runWithContext(fn) + : fn() + } + // TODO: refactor the whole before guards by internally using router.beforeEach function navigate( @@ -907,7 +916,9 @@ export function createRouter(options: RouterOptions): Router { ): void { // navigation is confirmed, call afterGuards // TODO: wrap with error handlers - for (const guard of afterGuards.list()) guard(to, from, failure) + for (const guard of afterGuards.list()) { + runWithContext(() => guard(to, from, failure)) + } } /** @@ -1263,14 +1274,15 @@ export function createRouter(options: RouterOptions): Router { }, } - return router -} + // TODO: type this as NavigationGuardReturn or similar instead of any + function runGuardQueue(guards: Lazy[]): Promise { + return guards.reduce( + (promise, guard) => promise.then(() => runWithContext(guard)), + Promise.resolve() + ) + } -function runGuardQueue(guards: Lazy[]): Promise { - return guards.reduce( - (promise, guard) => promise.then(() => guard()), - Promise.resolve() - ) + return router } function extractChangingRecords(