From 2238925fbea7a57d5a0e69e39ff00afdfce267db Mon Sep 17 00:00:00 2001 From: Dmitry Sharshakov Date: Fri, 18 Oct 2019 19:34:45 +0300 Subject: [PATCH] feat(core): validate directives names (#326) --- .../runtime-core/__tests__/apiApp.spec.ts | 32 +++++++++++++++++++ packages/runtime-core/src/apiApp.ts | 7 ++-- packages/runtime-core/src/component.ts | 8 ++++- packages/runtime-core/src/directives.ts | 12 ++++++- 4 files changed, 55 insertions(+), 4 deletions(-) diff --git a/packages/runtime-core/__tests__/apiApp.spec.ts b/packages/runtime-core/__tests__/apiApp.spec.ts index 3eb34020..4bcf3614 100644 --- a/packages/runtime-core/__tests__/apiApp.spec.ts +++ b/packages/runtime-core/__tests__/apiApp.spec.ts @@ -138,6 +138,11 @@ describe('api: createApp', () => { expect(spy1).toHaveBeenCalled() expect(spy2).not.toHaveBeenCalled() expect(spy3).toHaveBeenCalled() + + app.directive('bind', FooBar) + expect( + `Do not use built-in directive ids as custom directive id: bind` + ).toHaveBeenWarned() }) test('mixin', () => { @@ -342,6 +347,33 @@ describe('api: createApp', () => { ).toHaveBeenWarned() }) + test('Component.directives', () => { + const app = createApp() + Object.defineProperty(app.config, 'isNativeTag', { + value: isNativeTag, + writable: false + }) + + const Root = { + directives: { + bind: () => {} + }, + setup() { + return { + count: ref(0) + } + }, + render() { + return null + } + } + + app.mount(Root, nodeOps.createElement('div')) + expect( + `Do not use built-in directive ids as custom directive id: bind` + ).toHaveBeenWarned() + }) + test('register using app.component', () => { const app = createApp() Object.defineProperty(app.config, 'isNativeTag', { diff --git a/packages/runtime-core/src/apiApp.ts b/packages/runtime-core/src/apiApp.ts index 31d61047..18afa463 100644 --- a/packages/runtime-core/src/apiApp.ts +++ b/packages/runtime-core/src/apiApp.ts @@ -1,7 +1,7 @@ import { Component, Data, validateComponentName } from './component' import { ComponentOptions } from './apiOptions' import { ComponentPublicInstance } from './componentProxy' -import { Directive } from './directives' +import { Directive, validateDirectiveName } from './directives' import { RootRenderFunction } from './createRenderer' import { InjectionKey } from './apiInject' import { isFunction, NO } from '@vue/shared' @@ -127,7 +127,10 @@ export function createAppAPI( }, directive(name: string, directive?: Directive) { - // TODO directive name validation + if (__DEV__) { + validateDirectiveName(name) + } + if (!directive) { return context.directives[name] as any } else { diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 5e8539b8..b3dec82f 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -13,7 +13,7 @@ import { callWithAsyncErrorHandling } from './errorHandling' import { AppContext, createAppContext, AppConfig } from './apiApp' -import { Directive } from './directives' +import { Directive, validateDirectiveName } from './directives' import { applyOptions, ComponentOptions } from './apiOptions' import { EMPTY_OBJ, @@ -256,6 +256,12 @@ export function setupStatefulComponent( validateComponentName(name, instance.appContext.config) } } + if (Component.directives) { + const names = Object.keys(Component.directives) + for (let i = 0; i < names.length; i++) { + validateDirectiveName(names[i]) + } + } } // 0. create render proxy property access cache instance.accessCache = {} diff --git a/packages/runtime-core/src/directives.ts b/packages/runtime-core/src/directives.ts index 1ab1b0d8..01c9a832 100644 --- a/packages/runtime-core/src/directives.ts +++ b/packages/runtime-core/src/directives.ts @@ -12,7 +12,7 @@ return applyDirectives(h(comp), [ */ import { VNode, cloneVNode } from './vnode' -import { extend, isArray, isFunction, EMPTY_OBJ } from '@vue/shared' +import { extend, isArray, isFunction, EMPTY_OBJ, makeMap } from '@vue/shared' import { warn } from './warning' import { ComponentInternalInstance } from './component' import { currentRenderingInstance } from './componentRenderUtils' @@ -51,6 +51,16 @@ type DirectiveModifiers = Record const valueCache = new WeakMap>() +const isBuiltInDirective = /*#__PURE__*/ makeMap( + 'bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text' +) + +export function validateDirectiveName(name: string) { + if (isBuiltInDirective(name)) { + warn('Do not use built-in directive ids as custom directive id: ' + name) + } +} + function applyDirective( props: Record, instance: ComponentInternalInstance,