From: Eduardo San Martin Morote Date: Fri, 17 Apr 2020 18:29:27 +0000 (+0200) Subject: feat: add guards types X-Git-Tag: v4.0.0-alpha.7~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7ccd5a0e67d88467fc661474308fbdf55b947ec;p=thirdparty%2Fvuejs%2Frouter.git feat: add guards types --- diff --git a/__tests__/mount.ts b/__tests__/mount.ts index 6f8da1fc..5d6a6c4b 100644 --- a/__tests__/mount.ts +++ b/__tests__/mount.ts @@ -12,7 +12,6 @@ import { computed, markRaw, App, - ComponentOptionsWithProps, VNode, } from 'vue' import { compile } from '@vue/compiler-dom' @@ -23,7 +22,7 @@ import { routeLocationKey } from '../src/utils/injectionSymbols' export interface MountOptions { propsData: Record provide: Record - components: ComponentOptionsWithProps['components'] + components: Record slots: Record } diff --git a/package.json b/package.json index 7e3132fa..87f5b512 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ ] }, "peerDependencies": { - "vue": "^3.0.0-beta.1" + "vue": "^3.0.0-beta.2" }, "devDependencies": { "@microsoft/api-documenter": "^7.7.18", @@ -57,7 +57,7 @@ "@types/jsdom": "^16.2.1", "@types/webpack": "^4.41.11", "@types/webpack-env": "^1.15.0", - "@vue/compiler-sfc": "3.0.0-beta.1", + "@vue/compiler-sfc": "3.0.0-beta.2", "axios": "^0.19.2", "browserstack-local": "^1.4.5", "chalk": "^4.0.0", @@ -84,7 +84,7 @@ "ts-loader": "^7.0.0", "ts-node": "^8.8.1", "typescript": "^3.8.3", - "vue": "^3.0.0-beta.1", + "vue": "^3.0.0-beta.2", "vue-loader": "^16.0.0-alpha.3", "webpack": "^4.42.0", "webpack-cli": "^3.3.11", diff --git a/src/components/Link.ts b/src/components/Link.ts index a4f3aa10..9428b7e4 100644 --- a/src/components/Link.ts +++ b/src/components/Link.ts @@ -6,6 +6,7 @@ import { computed, reactive, unref, + Component, } from 'vue' import { RouteLocationRaw, VueUseOptions, RouteLocation } from '../types' import { isSameLocationObject, isSameRouteRecord } from '../utils' @@ -67,7 +68,7 @@ export function useLink(props: UseLinkOptions) { } } -export const Link = defineComponent({ +export const Link = (defineComponent({ name: 'RouterLink', props: { to: { @@ -110,7 +111,7 @@ export const Link = defineComponent({ ) } }, -}) +}) as unknown) as Component function guardEvent(e: MouseEvent) { // don't redirect with control keys diff --git a/src/components/View.ts b/src/components/View.ts index 5d5237de..9c2134e4 100644 --- a/src/components/View.ts +++ b/src/components/View.ts @@ -7,6 +7,7 @@ import { computed, ref, ComponentPublicInstance, + Component, } from 'vue' import { RouteLocationNormalizedLoaded } from '../types' import { @@ -15,7 +16,7 @@ import { routeLocationKey, } from '../utils/injectionSymbols' -export const View = defineComponent({ +export const View = (defineComponent({ name: 'RouterView', props: { name: { @@ -94,4 +95,4 @@ export const View = defineComponent({ : null } }, -}) +}) as unknown) as Component diff --git a/src/matcher/types.ts b/src/matcher/types.ts index 68e1af6f..ccf9d059 100644 --- a/src/matcher/types.ts +++ b/src/matcher/types.ts @@ -15,7 +15,7 @@ export interface RouteRecordNormalized { meta: Exclude props: Exclude<_RouteRecordBase['props'], void> beforeEnter: RouteRecordMultipleViews['beforeEnter'] - leaveGuards: NavigationGuard[] + leaveGuards: NavigationGuard[] instances: Record // can only be of of the same type as this record aliasOf: RouteRecordNormalized | undefined diff --git a/src/navigationGuards.ts b/src/navigationGuards.ts index 2bbd0ea0..57348eca 100644 --- a/src/navigationGuards.ts +++ b/src/navigationGuards.ts @@ -6,6 +6,8 @@ import { RouteLocationNormalizedLoaded, NavigationGuardNextCallback, isRouteLocation, + Lazy, + RouteComponent, } from './types' import { @@ -43,18 +45,10 @@ export function onBeforeRouteLeave(leaveGuard: NavigationGuard) { } export function guardToPromiseFn( - guard: NavigationGuard, + guard: NavigationGuard, to: RouteLocationNormalized, from: RouteLocationNormalizedLoaded, - instance?: undefined -): () => Promise -export function guardToPromiseFn< - ThisType extends ComponentPublicInstance | undefined ->( - guard: NavigationGuard, - to: RouteLocationNormalized, - from: RouteLocationNormalizedLoaded, - instance: ThisType + instance?: ComponentPublicInstance | undefined ): () => Promise { return () => new Promise((resolve, reject) => { @@ -112,7 +106,9 @@ export function extractComponentsGuards( const rawComponent = record.components[name] if (typeof rawComponent === 'function') { // start requesting the chunk already - const componentPromise = rawComponent().catch(() => null) + const componentPromise = (rawComponent as Lazy)().catch( + () => null + ) guards.push(async () => { const resolved = await componentPromise if (!resolved) throw new Error('TODO: error while fetching') @@ -121,7 +117,8 @@ export function extractComponentsGuards( : resolved // replace the function with the resolved component record.components[name] = resolvedComponent - const guard = resolvedComponent[guardType] + // @ts-ignore: the options types are not propagated to Component + const guard: NavigationGuard = resolvedComponent[guardType] return ( // @ts-ignore: the guards matched the instance type guard && guardToPromiseFn(guard, to, from, record.instances[name])() diff --git a/src/router.ts b/src/router.ts index 3278d66d..50a82ea8 100644 --- a/src/router.ts +++ b/src/router.ts @@ -12,6 +12,7 @@ import { RouteLocation, RouteRecordName, isRouteName, + NavigationGuardWithThis, } from './types' import { RouterHistory, HistoryState } from './history/common' import { @@ -103,7 +104,7 @@ export interface Router { push(to: RouteLocationRaw): Promise replace(to: RouteLocationRaw): Promise - beforeEach(guard: NavigationGuard): () => void + beforeEach(guard: NavigationGuardWithThis): () => void afterEach(guard: PostNavigationGuard): () => void onError(handler: ErrorHandler): () => void @@ -121,7 +122,7 @@ export function createRouter({ }: RouterOptions): Router { const matcher = createRouterMatcher(routes, {}) - const beforeGuards = useCallbacks>() + const beforeGuards = useCallbacks>() const afterGuards = useCallbacks() const currentRoute = ref( START_LOCATION_NORMALIZED @@ -607,6 +608,47 @@ export function createRouter({ return router } +declare module '@vue/runtime-core' { + interface ComponentCustomOptions { + /** + * Guard called when the router is navigating to the route that is rendering + * this component from a different route. Differently from `beforeRouteUpdate` + * and `beforeRouteLeave`, `beforeRouteEnter` does not have access to the + * component instance through `this` because it triggers before the component + * is even mounted. + * + * @param to - RouteLocationRaw we are navigating to + * @param from - RouteLocationRaw we are navigating from + * @param next - function to validate, cancel or modify (by redirecting) the + * navigation + */ + beforeRouteEnter?: NavigationGuardWithThis + + /** + * Guard called whenever the route that renders this component has changed but + * it is reused for the new route. This allows you to guard for changes in + * params, the query or the hash. + * + * @param to - RouteLocationRaw we are navigating to + * @param from - RouteLocationRaw we are navigating from + * @param next - function to validate, cancel or modify (by redirecting) the + * navigation + */ + beforeRouteUpdate?: NavigationGuard + + /** + * Guard called when the router is navigating away from the current route that + * is rendering this component. + * + * @param to - RouteLocationRaw we are navigating to + * @param from - RouteLocationRaw we are navigating from + * @param next - function to validate, cancel or modify (by redirecting) the + * navigation + */ + beforeRouteLeave?: NavigationGuard + } +} + function applyRouterPlugin(app: App, router: Router) { app.component('RouterLink', Link) app.component('RouterView', View) diff --git a/src/types/index.ts b/src/types/index.ts index 5edb6728..2b05605f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,12 +1,6 @@ import { LocationQuery, LocationQueryRaw } from '../utils/query' import { PathParserOptions } from '../matcher/path-parser-ranker' -import { - markRaw, - ComponentOptions, - ComponentPublicInstance, - Ref, - ComputedRef, -} from 'vue' +import { markRaw, Ref, ComputedRef, Component } from 'vue' import { RouteRecord, RouteRecordNormalized } from '../matcher/types' import { HistoryState } from '../history/common' @@ -134,41 +128,7 @@ export interface RouteLocationNormalized extends _RouteLocationBase { matched: RouteRecordNormalized[] // non-enumerable } -// interface PropsTransformer { -// (params: RouteParams): any -// } - -// export interface RouterLocation { -// record: RouteRecordRaw -// path: string -// params: ReturnType -// } - -// TODO: type this for beforeRouteUpdate and beforeRouteLeave -// TODO: support arrays -export interface RouteComponentInterface { - beforeRouteEnter?: NavigationGuard - /** - * Guard called when the router is navigating away from the current route - * that is rendering this component. - * @param to - RouteLocationRaw we are navigating to - * @param from - RouteLocationRaw we are navigating from - * @param next - function to validate, cancel or modify (by redirecting) the navigation - */ - beforeRouteLeave?: NavigationGuard - /** - * Guard called whenever the route that renders this component has changed but - * it is reused for the new route. This allows you to guard for changes in params, - * the query or the hash. - * @param to - RouteLocationRaw we are navigating to - * @param from - RouteLocationRaw we are navigating from - * @param next - function to validate, cancel or modify (by redirecting) the navigation - */ - beforeRouteUpdate?: NavigationGuard -} - -// TODO: allow defineComponent export type RouteComponent = (Component | ReturnType) & -export type RouteComponent = ComponentOptions & RouteComponentInterface +export type RouteComponent = Component export type RawRouteComponent = RouteComponent | Lazy export type RouteRecordName = string | symbol @@ -201,7 +161,9 @@ export interface _RouteRecordBase { | Record | ((to: RouteLocationNormalized) => Record) // TODO: beforeEnter has no effect with redirect, move and test - beforeEnter?: NavigationGuard | NavigationGuard[] + beforeEnter?: + | NavigationGuardWithThis + | NavigationGuardWithThis[] /** * Arbitrary data attached to the record. */ @@ -282,9 +244,8 @@ export interface NavigationGuardCallback { export type NavigationGuardNextCallback = (vm: any) => any -export interface NavigationGuard { +export interface NavigationGuard { ( - this: V, // TODO: we could maybe add extra information like replace: true/false to: RouteLocationNormalized, from: RouteLocationNormalized, @@ -292,6 +253,15 @@ export interface NavigationGuard { ): any } +export interface NavigationGuardWithThis { + ( + this: T, + to: RouteLocationNormalized, + from: RouteLocationNormalized, + next: NavigationGuardCallback + ): any +} + export interface PostNavigationGuard { ( to: RouteLocationNormalized, diff --git a/yarn.lock b/yarn.lock index a3369e50..804976eb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -842,34 +842,34 @@ dependencies: "@types/node" "*" -"@vue/compiler-core@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.0-beta.1.tgz#77017e0a98b808da3834327bcf8857e4b89363ef" - integrity sha512-mlHfX/+3qH+GAuMbGFvye0Jn2/H9IwbmX6oqbpLErM241xAVbpqniFDiJCywrfjeUDKMfDuNs1i6lGnDBH715g== +"@vue/compiler-core@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.0.0-beta.2.tgz#ac8f14856cd874cb22d89181e8c3bddf810b8261" + integrity sha512-/c3ePWU9T7xwn0J/YRlxCxnxqphNVlcXisnI6aMoK3pjvQFXOMD6tfrHUtepCrQLNdKlhxNjqe3Q625Z64Z0kQ== dependencies: "@babel/parser" "^7.8.6" "@babel/types" "^7.8.6" - "@vue/shared" "3.0.0-beta.1" + "@vue/shared" "3.0.0-beta.2" estree-walker "^0.8.1" source-map "^0.6.1" -"@vue/compiler-dom@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.0-beta.1.tgz#eabd8279e5433214e613558561f59e05a01ef450" - integrity sha512-Tp3Y2sT014B0Z3VpdWLwAv8o6kNPZOwFFMI2aAgIBmG3KWPGXUxm2LDRrNZWPoOPYPWYjRMBri1cNy3gA2MeCA== +"@vue/compiler-dom@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.0.0-beta.2.tgz#344263f4e50258496bfdbd01a9a85a7b842e96de" + integrity sha512-VZwvnsWPoi0/MR5CF5p5VhRGk3AkM9MQYt/pMaXDmbl09kWKVjQZubCRvpzuyjjm1QnV1yrSaqTZWDAIYbKYtw== dependencies: - "@vue/compiler-core" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/compiler-core" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" -"@vue/compiler-sfc@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.0-beta.1.tgz#1d1ae5e2d2ba83f5234d4b2f84e4ba4bd5741c27" - integrity sha512-Dv+cKKGMwGOpDzE9UuWe/jCerIdDxwd0CAtxgF2XrXULxNeHGSedmx7m2fkPLSKqqDfHtbPZuU55jWp4axL6wA== +"@vue/compiler-sfc@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.0.0-beta.2.tgz#610697d29e067165d5e2ccf81bdd238399b6d156" + integrity sha512-v3zQ8fR+Oc8U/WZFmJY641b05ayP1cdngZMn8PUgPecRwjQkdR/k9ikCMZpsrWtoS4am0e3PHyCt/BBZCxA4nA== dependencies: - "@vue/compiler-core" "3.0.0-beta.1" - "@vue/compiler-dom" "3.0.0-beta.1" - "@vue/compiler-ssr" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/compiler-core" "3.0.0-beta.2" + "@vue/compiler-dom" "3.0.0-beta.2" + "@vue/compiler-ssr" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" consolidate "^0.15.1" hash-sum "^2.0.0" lru-cache "^5.1.1" @@ -878,42 +878,42 @@ postcss-selector-parser "^6.0.2" source-map "^0.6.1" -"@vue/compiler-ssr@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.0-beta.1.tgz#e0cfde96de4d41809d59102e7f1f0f860e731cde" - integrity sha512-vO4r0/P3G/LfLgJGKrMh9MD1wExCajrAE5nNlqAkWduIYY+4fegF7UyO67dU+ji1YShxrPfDYJ8vcSb4XTKXxQ== +"@vue/compiler-ssr@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.0.0-beta.2.tgz#05c965d8f5a6362928959d4f8a7b75b07cf68c45" + integrity sha512-+g0u8Zgns3uR6E3338yCGBz927mOdzzsCFxBKTS6O2WRXhUZ1vcRerigruRRF+LDiWtFm2txsjj70CcMqF4D/w== dependencies: - "@vue/compiler-dom" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/compiler-dom" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" -"@vue/reactivity@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.0-beta.1.tgz#b4899fe95efbc8ed15343f9d67950f2a33e219dc" - integrity sha512-XbitRfUKIAgPO0IXEbi9/mq93xOL5CGRjaTPH2DvvTU0hXlAeW8aMPQe3+R+vvXppBiFERY3yjZjQc5SYkZVsg== +"@vue/reactivity@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.0.0-beta.2.tgz#216922e264dc447a508f756374c09fb26a7cbe1c" + integrity sha512-vAYHiBmpHfLAJulssTTTlvdHtyyKeOCG3qCu/TxDhu3NFHEfrt4eytR2atR6EbpTb853/QKcoW3k6L6g/znxAw== dependencies: - "@vue/shared" "3.0.0-beta.1" + "@vue/shared" "3.0.0-beta.2" -"@vue/runtime-core@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.0-beta.1.tgz#a3d97183dd5bc022fc02b9a86180698dc11a2e65" - integrity sha512-eNHHGT17i2dOtAQpmn8QRDAcVGS4TiCyjGW+v6GfSlHvip1fFUmawD9pLSPVdL3DngGm9SmioOVmBK2CB8JG0g== +"@vue/runtime-core@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.0.0-beta.2.tgz#0413292d8746c532527dbcdbfcbc56be312f05ad" + integrity sha512-tcxNNV7y+H2046F79iw90OdwyxvMEqJ5iOQTsOvfVf2hBNrCWPG1tAU+kywFBlBY4I26k7XB/Q1ZdHb2q8YLaA== dependencies: - "@vue/reactivity" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/reactivity" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" -"@vue/runtime-dom@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.0-beta.1.tgz#f93565cccdc0a1d2f419836d5468989a7a868c66" - integrity sha512-+dnJS7582V6iZLm8YqsXjabL1eXo3n8ltzxWZlI/nV1ZPSWKQJpvB6ICbdRjwOlka4lukYStca3ZsrYQI7SvVg== +"@vue/runtime-dom@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.0.0-beta.2.tgz#957a59ecfcdf54619e4d40d2e2967587ea58728d" + integrity sha512-swWm7fa3JKEGE0KYVevYqaBTSGxx/bmlwJTbJcnnNgdZZGtWUQsUzXCk6JKRuoYjy+iU0ONcHidEhpwdazH9Aw== dependencies: - "@vue/runtime-core" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/runtime-core" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" csstype "^2.6.8" -"@vue/shared@3.0.0-beta.1": - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.0-beta.1.tgz#234185d345cde7e05db569e653feae87912bf218" - integrity sha512-LehYUPtykdL3ZEUto2W1n26k+opBT1gtFdESPtp4lGo9oIreLl3pJVoUPIkY/Kk/mOZGmBOsYXiLZGJvfmGEQw== +"@vue/shared@3.0.0-beta.2": + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.0-beta.2.tgz#6659413868b0ba0b96260cbf3c9ed99f2d0f90ef" + integrity sha512-ED5oa+ZOcjAJkWEzL0zFZ4QG89L23DrW9LBBGT6YBUhBmOsf9BKii2JIBfdxWYwRkjAhbHffQH0mc6rI2famug== "@webassemblyjs/ast@1.9.0": version "1.9.0" @@ -9001,14 +9001,14 @@ vue-loader@^16.0.0-alpha.3: merge-source-map "^1.1.0" source-map "^0.6.1" -vue@^3.0.0-beta.1: - version "3.0.0-beta.1" - resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.0-beta.1.tgz#86a4b8431ab4b7523487cad4ec0d7235fead22dc" - integrity sha512-Z5SFtxSQNYwtyfMWxAiJYoumOwRnRdha6opDaRy8f4jhJAGn9tpi2muJLxE4m6QZ9UqjnHghUC3VjxzXfofQTQ== +vue@^3.0.0-beta.2: + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.0-beta.2.tgz#f808b498d10cbebe4007738aa2130bd0802f393d" + integrity sha512-/HYhK9i8PWM0fL38YflFK/vY1ots+JyNI2GZa8VtDqj4jD3+2UZ0CH0kjmw9YTmRtHdsU65CXGkVuA3EMV3mXQ== dependencies: - "@vue/compiler-dom" "3.0.0-beta.1" - "@vue/runtime-dom" "3.0.0-beta.1" - "@vue/shared" "3.0.0-beta.1" + "@vue/compiler-dom" "3.0.0-beta.2" + "@vue/runtime-dom" "3.0.0-beta.2" + "@vue/shared" "3.0.0-beta.2" w3c-hr-time@^1.0.1: version "1.0.2"