diff --git a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts index 1d11c6e71..b10a32247 100644 --- a/packages/router/__tests__/guards/extractComponentsGuards.spec.ts +++ b/packages/router/__tests__/guards/extractComponentsGuards.spec.ts @@ -104,4 +104,30 @@ describe('extractComponentsGuards', () => { 'custom' ) }) + + it('preserves resolved modules in mods', async () => { + const mod = { + default: components.Home, + __esModule: true, + custom: true, + } + const mod2 = { + default: components.Bar, + __esModule: true, + custom: true, + } + const record = normalizeRouteRecord({ + path: '/', + components: { default: async () => mod, other: async () => mod2 }, + }) + expect(record.mods).toEqual({}) + const guards = extractComponentsGuards( + [record], + 'beforeRouteEnter', + to, + from + ) + await Promise.all(guards.map(guard => guard())) + expect(record.mods).toEqual({ default: mod, other: mod2 }) + }) }) diff --git a/packages/router/__tests__/guards/loadRouteLocation.spec.ts b/packages/router/__tests__/guards/loadRouteLocation.spec.ts index af7f1cee8..2921dc730 100644 --- a/packages/router/__tests__/guards/loadRouteLocation.spec.ts +++ b/packages/router/__tests__/guards/loadRouteLocation.spec.ts @@ -86,6 +86,61 @@ describe('loadRouteLocation', () => { ]) }) + describe('mods', () => { + const mod = { + default: components.Home, + __esModule: true, + custom: true, + } + const mod2 = { + default: FunctionalHome, + __esModule: true, + custom: true, + } + + it('preserves resolved modules', async () => { + const router = createRouter({ + history: createMemoryHistory(), + routes: [ + { + path: '/', + component: async () => mod, + }, + ], + }) + + const loaded = await loadRouteLocation(router.resolve('/')) + // mods follow the same structure as components + expect(loaded.matched[0]?.mods).toEqual({ + default: expect.anything(), + }) + expect(loaded.matched[0]?.mods?.default).toBe(mod) + }) + + it('preserves resolved modules for named components', async () => { + const router = createRouter({ + history: createMemoryHistory(), + routes: [ + { + path: '/', + components: { + default: async () => mod2, + name: async () => mod, + }, + }, + ], + }) + + const loaded = await loadRouteLocation(router.resolve('/')) + expect(loaded.matched[0]?.mods).toEqual({ + default: expect.anything(), + name: expect.anything(), + }) + expect(loaded.matched[0]?.mods?.name).toBe(mod) + expect(loaded.matched[0]?.mods?.default).toBe(mod2) + }) + }) + it('throws with non loadable routes', async () => { expect.assertions(1) await expect( diff --git a/packages/router/src/matcher/index.ts b/packages/router/src/matcher/index.ts index da9e821f3..71614b9ec 100644 --- a/packages/router/src/matcher/index.ts +++ b/packages/router/src/matcher/index.ts @@ -394,6 +394,7 @@ export function normalizeRouteRecord( leaveGuards: new Set(), updateGuards: new Set(), enterCallbacks: {}, + mods: {}, components: 'components' in record ? record.components || null diff --git a/packages/router/src/matcher/types.ts b/packages/router/src/matcher/types.ts index 5efab0559..5f80f27bf 100644 --- a/packages/router/src/matcher/types.ts +++ b/packages/router/src/matcher/types.ts @@ -31,6 +31,13 @@ export interface RouteRecordNormalized { * {@inheritDoc RouteRecordMultipleViews.components} */ components: RouteRecordMultipleViews['components'] | null | undefined + + /** + * Contains the original modules for lazy loaded components. + * @internal + */ + mods: Record + /** * Nested route records. */ diff --git a/packages/router/src/navigationGuards.ts b/packages/router/src/navigationGuards.ts index 3c421590b..90c079f70 100644 --- a/packages/router/src/navigationGuards.ts +++ b/packages/router/src/navigationGuards.ts @@ -316,14 +316,14 @@ export function extractComponentsGuards( guards.push(() => componentPromise.then(resolved => { if (!resolved) - return Promise.reject( - new Error( - `Couldn't resolve component "${name}" at "${record.path}"` - ) + throw new Error( + `Couldn't resolve component "${name}" at "${record.path}"` ) const resolvedComponent = isESModule(resolved) ? resolved.default : resolved + // keep the resolved module for plugins like data loaders + record.mods[name] = resolved // replace the function with the resolved component // cannot be null or undefined because we went into the for loop record.components![name] = resolvedComponent @@ -331,6 +331,7 @@ export function extractComponentsGuards( const options: ComponentOptions = (resolvedComponent as any).__vccOpts || resolvedComponent const guard = options[guardType] + return ( guard && guardToPromiseFn(guard, to, from, record, name, runWithContext)() @@ -373,9 +374,12 @@ export function loadRouteLocation( `Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.` ) ) + const resolvedComponent = isESModule(resolved) ? resolved.default : resolved + // keep the resolved module for plugins like data loaders + record.mods[name] = resolved // replace the function with the resolved component // cannot be null or undefined because we went into the for loop record.components![name] = resolvedComponent