]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: v-on with no argument
authorEvan You <yyx990803@gmail.com>
Wed, 25 Sep 2019 00:51:48 +0000 (20:51 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 25 Sep 2019 00:51:48 +0000 (20:51 -0400)
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/src/errors.ts
packages/compiler-core/src/runtimeConstants.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/runtime-core/src/helpers/toHandlers.ts [new file with mode: 0644]
packages/runtime-core/src/index.ts

index af39a3b8d6b3c04cad4adeb7fb4be5f3e8f0c630..00152a751a410f4c62c2bbec2ff4e23b861d3948 100644 (file)
@@ -11,7 +11,8 @@ import {
   CREATE_VNODE,
   MERGE_PROPS,
   RESOLVE_DIRECTIVE,
-  APPLY_DIRECTIVES
+  APPLY_DIRECTIVES,
+  TO_HANDLERS
 } from '../../src/runtimeConstants'
 import {
   CallExpression,
@@ -198,6 +199,67 @@ describe('compiler: element transform', () => {
     })
   })
 
+  test('v-on="obj"', () => {
+    const { root, node } = parseWithElementTransform(
+      `<div id="foo" v-on="obj" class="bar" />`
+    )
+    expect(root.imports).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
+    expect(node.arguments[1]).toMatchObject({
+      type: NodeTypes.JS_CALL_EXPRESSION,
+      callee: MERGE_PROPS,
+      arguments: [
+        createStaticObjectMatcher({
+          id: 'foo'
+        }),
+        {
+          type: NodeTypes.JS_CALL_EXPRESSION,
+          callee: TO_HANDLERS,
+          arguments: [
+            {
+              type: NodeTypes.EXPRESSION,
+              content: `obj`
+            }
+          ]
+        },
+        createStaticObjectMatcher({
+          class: 'bar'
+        })
+      ]
+    })
+  })
+
+  test('v-on="obj" + v-bind="obj"', () => {
+    const { root, node } = parseWithElementTransform(
+      `<div id="foo" v-on="handlers" v-bind="obj" />`
+    )
+    expect(root.imports).toContain(MERGE_PROPS)
+    expect(node.callee).toBe(CREATE_VNODE)
+    expect(node.arguments[1]).toMatchObject({
+      type: NodeTypes.JS_CALL_EXPRESSION,
+      callee: MERGE_PROPS,
+      arguments: [
+        createStaticObjectMatcher({
+          id: 'foo'
+        }),
+        {
+          type: NodeTypes.JS_CALL_EXPRESSION,
+          callee: TO_HANDLERS,
+          arguments: [
+            {
+              type: NodeTypes.EXPRESSION,
+              content: `handlers`
+            }
+          ]
+        },
+        {
+          type: NodeTypes.EXPRESSION,
+          content: `obj`
+        }
+      ]
+    })
+  })
+
   test('error on v-bind with no argument', () => {
     const onError = jest.fn()
     parseWithElementTransform(`<div v-bind/>`, { onError })
index 2f28f5f797fe44d2ce90a6c5ebae85d869d231f8..08424542771ba72b716bd48c3af791dfee5ae325 100644 (file)
@@ -67,6 +67,7 @@ export const enum ErrorCodes {
   X_FOR_NO_EXPRESSION,
   X_FOR_MALFORMED_EXPRESSION,
   X_V_BIND_NO_EXPRESSION,
+  X_V_ON_NO_EXPRESSION,
 
   // generic errors
   X_PREFIX_ID_NOT_SUPPORTED
@@ -133,6 +134,8 @@ export const errorMessages: { [code: number]: string } = {
   [ErrorCodes.X_ELSE_NO_ADJACENT_IF]: `v-else has no adjacent v-if`,
   [ErrorCodes.X_FOR_NO_EXPRESSION]: `v-for has no expression`,
   [ErrorCodes.X_FOR_MALFORMED_EXPRESSION]: `v-for has invalid expression`,
+  [ErrorCodes.X_V_BIND_NO_EXPRESSION]: `v-bind is missing expression`,
+  [ErrorCodes.X_V_ON_NO_EXPRESSION]: `v-on is missing expression`,
 
   // generic errors
   [ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED]: `"prefixIdentifiers" option is not supported in this build of compiler because it is optimized for payload size.`
index 74178b95680400214402b18428ef980385d6f531..2423c7d73cad1a91a5d59e93eb26e1c738ddb779 100644 (file)
@@ -13,3 +13,4 @@ export const RENDER_LIST = `renderList`
 export const CAPITALIZE = `capitalize`
 export const TO_STRING = `toString`
 export const MERGE_PROPS = `mergeProps`
+export const TO_HANDLERS = `toHandlers`
index fe381dedca0b9a0bc03e7f8b47a544ff0b003677..7b73411c7409bd9ab1e0679a96a2ec0e80bcd9a5 100644 (file)
@@ -21,7 +21,8 @@ import {
   APPLY_DIRECTIVES,
   RESOLVE_DIRECTIVE,
   RESOLVE_COMPONENT,
-  MERGE_PROPS
+  MERGE_PROPS,
+  TO_HANDLERS
 } from '../runtimeConstants'
 
 const toValidId = (str: string): string => str.replace(/[^\w]/g, '')
@@ -97,15 +98,17 @@ export const transformElement: NodeTransform = (node, context) => {
   }
 }
 
+type PropsExpression = ObjectExpression | CallExpression | ExpressionNode
+
 function buildProps(
-  { loc, props }: ElementNode,
+  { loc: elementLoc, props }: ElementNode,
   context: TransformContext
 ): {
-  props: ObjectExpression | CallExpression | ExpressionNode
+  props: PropsExpression
   directives: DirectiveNode[]
 } {
   let properties: ObjectExpression['properties'] = []
-  const mergeArgs: Array<ObjectExpression | ExpressionNode> = []
+  const mergeArgs: PropsExpression[] = []
   const runtimeDirectives: DirectiveNode[] = []
 
   for (let i = 0; i < props.length; i++) {
@@ -126,24 +129,43 @@ function buildProps(
       )
     } else {
       // directives
-      // special case for v-bind with no argument
-      if (prop.name === 'bind' && !prop.arg) {
-        if (prop.exp) {
+      const { name, arg, exp, loc } = prop
+      // special case for v-bind and v-on with no argument
+      const isBind = name === 'bind'
+      if (!arg && (isBind || name === 'on')) {
+        if (exp) {
           if (properties.length) {
-            mergeArgs.push(createObjectExpression(properties, loc))
+            mergeArgs.push(createObjectExpression(properties, elementLoc))
             properties = []
           }
-          mergeArgs.push(prop.exp)
+          if (isBind) {
+            mergeArgs.push(exp)
+          } else {
+            // v-on="obj" -> toHandlers(obj)
+            context.imports.add(TO_HANDLERS)
+            mergeArgs.push({
+              type: NodeTypes.JS_CALL_EXPRESSION,
+              loc,
+              callee: TO_HANDLERS,
+              arguments: [exp]
+            })
+          }
         } else {
           context.onError(
-            createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc)
+            createCompilerError(
+              isBind
+                ? ErrorCodes.X_V_BIND_NO_EXPRESSION
+                : ErrorCodes.X_V_ON_NO_EXPRESSION,
+              loc
+            )
           )
         }
         continue
       }
 
-      const directiveTransform = context.directiveTransforms[prop.name]
+      const directiveTransform = context.directiveTransforms[name]
       if (directiveTransform) {
+        // has built-in directive transform.
         const { props, needRuntime } = directiveTransform(prop, context)
         if (isArray(props)) {
           properties.push(...props)
@@ -160,26 +182,26 @@ function buildProps(
     }
   }
 
-  let ret: ObjectExpression | CallExpression | ExpressionNode
+  let propsExpression: PropsExpression
 
-  // has v-bind="object", wrap with mergeProps
+  // has v-bind="object" or v-on="object", wrap with mergeProps
   if (mergeArgs.length) {
     if (properties.length) {
-      mergeArgs.push(createObjectExpression(properties, loc))
+      mergeArgs.push(createObjectExpression(properties, elementLoc))
     }
     if (mergeArgs.length > 1) {
       context.imports.add(MERGE_PROPS)
-      ret = createCallExpression(MERGE_PROPS, mergeArgs, loc)
+      propsExpression = createCallExpression(MERGE_PROPS, mergeArgs, elementLoc)
     } else {
       // single v-bind with nothing else - no need for a mergeProps call
-      ret = mergeArgs[0]
+      propsExpression = mergeArgs[0]
     }
   } else {
-    ret = createObjectExpression(properties, loc)
+    propsExpression = createObjectExpression(properties, elementLoc)
   }
 
   return {
-    props: ret,
+    props: propsExpression,
     directives: runtimeDirectives
   }
 }
diff --git a/packages/runtime-core/src/helpers/toHandlers.ts b/packages/runtime-core/src/helpers/toHandlers.ts
new file mode 100644 (file)
index 0000000..777a4f0
--- /dev/null
@@ -0,0 +1,15 @@
+import { isObject } from '@vue/shared'
+import { warn } from '../warning'
+
+// For prefixing keys in v-on="obj" with "on"
+export function toHandlers(obj: Record<string, any>): Record<string, any> {
+  const ret: Record<string, any> = {}
+  if (__DEV__ && !isObject(obj)) {
+    warn(`v-on with no argument expects an object value.`)
+    return ret
+  }
+  for (const key in obj) {
+    ret[`on${key}`] = obj[key]
+  }
+  return ret
+}
index fe9b519cfe48dd6960660d8c67243b040ac56d60..3f0896d7d471d2a0efef17a347f25028c6ec6d1a 100644 (file)
@@ -41,6 +41,7 @@ export { applyDirectives } from './directives'
 export { resolveComponent, resolveDirective } from './helpers/resolveAssets'
 export { renderList } from './helpers/renderList'
 export { toString } from './helpers/toString'
+export { toHandlers } from './helpers/toHandlers'
 export { capitalize } from '@vue/shared'
 
 // Internal, for integration with runtime compiler