From: Eduardo San Martin Morote Date: Tue, 6 Oct 2020 12:55:42 +0000 (+0200) Subject: feat(warn): improve warning for invalid components X-Git-Tag: v4.0.0-rc.1~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5985b6560d40412d67311df10343ee6a119a0535;p=thirdparty%2Fvuejs%2Frouter.git feat(warn): improve warning for invalid components Close #517 --- diff --git a/__tests__/guards/extractComponentsGuards.spec.ts b/__tests__/guards/extractComponentsGuards.spec.ts index 19de6062..7301ea47 100644 --- a/__tests__/guards/extractComponentsGuards.spec.ts +++ b/__tests__/guards/extractComponentsGuards.spec.ts @@ -3,6 +3,7 @@ import { START_LOCATION_NORMALIZED, RouteRecordRaw } from '../../src/types' import { components } from '../utils' import { normalizeRouteRecord } from '../../src/matcher' import { RouteRecordNormalized } from 'src/matcher/types' +import { mockWarn } from 'jest-mock-warn' const beforeRouteEnter = jest.fn() @@ -11,6 +12,16 @@ const to = START_LOCATION_NORMALIZED const from = START_LOCATION_NORMALIZED const NoGuard: RouteRecordRaw = { path: '/', component: components.Home } +const InvalidRoute: RouteRecordRaw = { + path: '/', + // @ts-ignore: intended error + component: null, +} +const WrongLazyRoute: RouteRecordRaw = { + path: '/', + // @ts-ignore: intended error + component: Promise.resolve(components.Home), +} const SingleGuard: RouteRecordRaw = { path: '/', component: { ...components.Home, beforeRouteEnter }, @@ -52,6 +63,8 @@ async function checkGuards( } describe('extractComponentsGuards', () => { + mockWarn() + it('extracts guards from one single component', async () => { await checkGuards([SingleGuard], 1) }) @@ -69,4 +82,18 @@ describe('extractComponentsGuards', () => { await checkGuards([SingleGuard, SingleGuard], 2) await checkGuards([SingleGuardNamed, SingleGuardNamed], 4) }) + + it('throws if component is null', async () => { + // @ts-ignore + await expect(checkGuards([InvalidRoute], 2)).rejects.toHaveProperty( + 'message', + expect.stringMatching('Invalid route component') + ) + expect('is not a valid component').toHaveBeenWarned() + }) + + it('warns wrong lazy component', async () => { + await checkGuards([WrongLazyRoute], 0, 1) + expect('Promise instead of a function').toHaveBeenWarned() + }) }) diff --git a/src/navigationGuards.ts b/src/navigationGuards.ts index 0ffb9e08..41046282 100644 --- a/src/navigationGuards.ts +++ b/src/navigationGuards.ts @@ -18,10 +18,11 @@ import { NavigationRedirectError, } from './errors' import { ComponentOptions, onUnmounted, onActivated, onDeactivated } from 'vue' -import { inject, getCurrentInstance, warn } from 'vue' +import { inject, getCurrentInstance } from 'vue' import { matchedRouteKey } from './injectionSymbols' import { RouteRecordNormalized } from './matcher/types' import { isESModule } from './utils' +import { warn } from './warning' function registerGuard(list: NavigationGuard[], guard: NavigationGuard) { const removeFromList = () => { @@ -225,13 +226,32 @@ export function extractComponentsGuards( for (const record of matched) { for (const name in record.components) { let rawComponent = record.components[name] - // warn if user wrote import('/component.vue') instead of () => import('./component.vue') - if (__DEV__ && 'then' in rawComponent) { - warn( - `Component "${name}" in record with path "${record.path}" is a Promise instead of a function that returns a Promise. Did you write "import('./MyPage.vue')" instead of "() => import('./MyPage.vue')"? This will break in production if not fixed.` - ) - let promise = rawComponent - rawComponent = () => promise + if (__DEV__) { + if ( + !rawComponent || + (typeof rawComponent !== 'object' && + typeof rawComponent !== 'function') + ) { + warn( + `Component "${name}" in record with path "${record.path}" is not` + + ` a valid component. Received "${String(rawComponent)}".` + ) + // throw to ensure we stop here but warn to ensure the message isn't + // missed by the user + throw new Error('Invalid route component') + } else if ('then' in rawComponent) { + // warn if user wrote import('/component.vue') instead of () => + // import('./component.vue') + warn( + `Component "${name}" in record with path "${record.path}" is a ` + + `Promise instead of a function that returns a Promise. Did you ` + + `write "import('./MyPage.vue')" instead of ` + + `"() => import('./MyPage.vue')" ? This will break in ` + + `production if not fixed.` + ) + let promise = rawComponent + rawComponent = () => promise + } } // skip update and leave guards if the route component is not mounted