]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test: tests for v-on transform
authorEvan You <yyx990803@gmail.com>
Wed, 25 Sep 2019 02:39:20 +0000 (22:39 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 25 Sep 2019 02:39:20 +0000 (22:39 -0400)
packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/transforms/vOn.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-core/src/transforms/vOn.ts
packages/compiler-core/src/utils.ts

index 08c0b3e928b05f740be8254da42918b467e98465..765602697fa0e96a856d528f7d3b8f4b4a8a0b7a 100644 (file)
@@ -76,9 +76,9 @@ return function render() {
 exports[`compiler: codegen ifNode 1`] = `
 "return function render() {
   with (this) {
-    return (foo)
+    return foo
       ? \\"foo\\"
-      : (bar)
+      : (a + b)
         ? toString(bye)
         : createVNode(Comment, 0, \\"foo\\")
   }
@@ -88,9 +88,9 @@ exports[`compiler: codegen ifNode 1`] = `
 exports[`compiler: codegen ifNode with no v-else 1`] = `
 "return function render() {
   with (this) {
-    return (foo)
+    return foo
       ? \\"foo\\"
-      : (bar)
+      : (a + b)
         ? toString(bye)
         : null
   }
index 38ef6b0675059436d8f2352e30408c97c5c931a8..43d5d0b43cb5a79db60ba865317e53b2ff427990 100644 (file)
@@ -191,7 +191,7 @@ describe('compiler: codegen', () => {
               },
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('bar', false, mockLoc),
+                condition: createExpression('a + b', false, mockLoc),
                 loc: mockLoc,
                 isRoot: true,
                 children: [createExpression(`bye`, false, mockLoc, true)]
@@ -215,9 +215,9 @@ describe('compiler: codegen', () => {
       })
     )
     expect(code).toMatch(`
-    return (foo)
+    return foo
       ? "foo"
-      : (bar)
+      : (a + b)
         ? ${TO_STRING}(bye)
         : ${CREATE_VNODE}(${COMMENT}, 0, "foo")`)
     expect(code).toMatchSnapshot()
@@ -248,7 +248,7 @@ describe('compiler: codegen', () => {
               },
               {
                 type: NodeTypes.IF_BRANCH,
-                condition: createExpression('bar', false, mockLoc),
+                condition: createExpression('a + b', false, mockLoc),
                 loc: mockLoc,
                 isRoot: true,
                 children: [createExpression(`bye`, false, mockLoc, true)]
@@ -259,9 +259,9 @@ describe('compiler: codegen', () => {
       })
     )
     expect(code).toMatch(`
-    return (foo)
+    return foo
       ? "foo"
-      : (bar)
+      : (a + b)
         ? ${TO_STRING}(bye)
         : null`)
     expect(code).toMatchSnapshot()
index b85b515108dfcb5aaaf7d073babfaea09fa156ce..41159d97af3e8dc38a8d46babf589b116b26d523 100644 (file)
@@ -1 +1,125 @@
-test.todo('v-on')
+import {
+  parse,
+  transform,
+  ElementNode,
+  ObjectExpression,
+  CompilerOptions,
+  ErrorCodes
+} from '../../src'
+import { transformOn } from '../../src/transforms/vOn'
+import { transformElement } from '../../src/transforms/transformElement'
+import { transformExpression } from '../../src/transforms/transformExpression'
+
+function parseWithVOn(
+  template: string,
+  options: CompilerOptions = {}
+): ElementNode {
+  const ast = parse(template)
+  transform(ast, {
+    nodeTransforms: [transformExpression, transformElement],
+    directiveTransforms: {
+      on: transformOn
+    },
+    ...options
+  })
+  return ast.children[0] as ElementNode
+}
+
+describe('compiler: transform v-bind', () => {
+  test('basic', () => {
+    const node = parseWithVOn(`<div v-on:click="onClick"/>`)
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        content: `onClick`,
+        isStatic: true,
+        loc: {
+          start: {
+            line: 1,
+            column: 11
+          },
+          end: {
+            line: 1,
+            column: 16
+          }
+        }
+      },
+      value: {
+        content: `onClick`,
+        isStatic: false,
+        loc: {
+          start: {
+            line: 1,
+            column: 17
+          },
+          end: {
+            line: 1,
+            column: 26
+          }
+        }
+      },
+      loc: {
+        start: {
+          line: 1,
+          column: 6
+        },
+        end: {
+          line: 1,
+          column: 26
+        }
+      }
+    })
+  })
+
+  test('dynamic arg', () => {
+    const node = parseWithVOn(`<div v-on:[event]="handler"/>`)
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        content: `"on" + event`,
+        isStatic: false
+      },
+      value: {
+        content: `handler`,
+        isStatic: false
+      }
+    })
+  })
+
+  test('dynamic arg with prefixing', () => {
+    const node = parseWithVOn(`<div v-on:[event]="handler"/>`, {
+      prefixIdentifiers: true
+    })
+    const props = node.codegenNode!.arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: {
+        isStatic: false,
+        children: [`"on" + `, `_ctx.`, { content: `event` }]
+      },
+      value: {
+        content: `handler`,
+        isStatic: false
+      }
+    })
+  })
+
+  test('should error if no expression', () => {
+    const onError = jest.fn()
+    parseWithVOn(`<div v-on />`, { onError })
+    expect(onError.mock.calls[0][0]).toMatchObject({
+      code: ErrorCodes.X_V_ON_NO_EXPRESSION,
+      loc: {
+        start: {
+          line: 1,
+          column: 6
+        },
+        end: {
+          line: 1,
+          column: 10
+        }
+      }
+    })
+  })
+
+  test.todo('.once modifier')
+})
index 559e3942c811e62cd825532c8d4639ec340ddf00..0706a1505c8964bdbc18da49192b75aeb1e6f36d 100644 (file)
@@ -15,7 +15,11 @@ import {
   IfBranchNode
 } from './ast'
 import { SourceMapGenerator, RawSourceMap } from 'source-map'
-import { advancePositionWithMutation, assert } from './utils'
+import {
+  advancePositionWithMutation,
+  assert,
+  isSimpleIdentifier
+} from './utils'
 import { isString, isArray } from '@vue/shared'
 import {
   RENDER_LIST,
@@ -325,7 +329,7 @@ function genExpressionAsPropertyKey(
     push(`]`)
   } else if (isStatic) {
     // only quote keys if necessary
-    const text = /^\d|[^\w]/.test(content) ? JSON.stringify(content) : content
+    const text = isSimpleIdentifier(content) ? content : JSON.stringify(content)
     push(text, node)
   } else {
     push(`[${content}]`, node)
@@ -366,9 +370,10 @@ function genIfBranch(
   if (condition) {
     // v-if or v-else-if
     const { push, indent, deindent, newline } = context
-    push(`(`)
+    const needsQuote = !isSimpleIdentifier(condition.content)
+    needsQuote && push(`(`)
     genExpression(condition, context)
-    push(`)`)
+    needsQuote && push(`)`)
     indent()
     context.indentLevel++
     push(`? `)
index 127b29c251926513e742d70cf392edee71de0e2e..cc77e8bc2bda265ea892c86ca56203cc99442132 100644 (file)
@@ -17,22 +17,21 @@ export function compile(
   template: string | RootNode,
   options: CompilerOptions = {}
 ): CodegenResult {
-  const ast = isString(template) ? parse(template, options) : template
-  const prefixIdentifiers = !__BROWSER__ && options.prefixIdentifiers === true
-
-  if (__BROWSER__ && options.prefixIdentifiers === false) {
+  if (__BROWSER__ && options.prefixIdentifiers) {
     ;(options.onError || defaultOnError)(
       createCompilerError(ErrorCodes.X_PREFIX_ID_NOT_SUPPORTED)
     )
   }
 
+  const ast = isString(template) ? parse(template, options) : template
+
   transform(ast, {
     ...options,
-    prefixIdentifiers,
+    prefixIdentifiers: !__BROWSER__ && options.prefixIdentifiers === true,
     nodeTransforms: [
       transformIf,
       transformFor,
-      ...(prefixIdentifiers ? [transformExpression] : []),
+      transformExpression,
       transformElement,
       ...(options.nodeTransforms || []) // user transforms
     ],
index feee19423e2b2aa29fc47fc18075561f7ad638cf..12dbf51513399c3e6a0477f79520f736b98868fd 100644 (file)
@@ -56,6 +56,9 @@ export function processExpression(
   node: ExpressionNode,
   context: TransformContext
 ) {
+  if (!context.prefixIdentifiers) {
+    return
+  }
   // lazy require dependencies so that they don't end up in rollup's dep graph
   // and thus can be tree-shaken in browser builds.
   const parseScript =
@@ -155,6 +158,9 @@ export function processExpression(
   })
 
   if (children.length) {
+    // mark it empty so that it's more noticeable in case another transform or
+    // codegen forget to handle `.children` first.
+    node.content = __DEV__ ? `[[REMOVED]]` : ``
     node.children = children
   }
 }
index a7e38b99afb5e020a35874e8df4a8d6339c117fa..db3b51ee4df3882ef472294f34e604a9480538c8 100644 (file)
@@ -1,14 +1,33 @@
 import { DirectiveTransform } from '../transform'
-import { createObjectProperty, createExpression } from '../ast'
+import { createObjectProperty, createExpression, ExpressionNode } from '../ast'
 import { capitalize } from '@vue/shared'
+import { createCompilerError, ErrorCodes } from '../errors'
+import { isSimpleIdentifier } from '../utils'
 
 // v-on without arg is handled directly in ./element.ts due to it affecting
 // codegen for the entire props object. This transform here is only for v-on
 // *with* args.
-export const transformOn: DirectiveTransform = ({ arg, exp, loc }) => {
-  const eventName = arg!.isStatic
-    ? createExpression(`on${capitalize(arg!.content)}`, true, arg!.loc)
-    : createExpression(`'on' + (${arg!.content})`, false, arg!.loc)
+export const transformOn: DirectiveTransform = ({ arg, exp, loc }, context) => {
+  if (!exp) {
+    context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
+  }
+  const { content, children, isStatic, loc: argLoc } = arg!
+  let eventName: ExpressionNode
+  if (isStatic) {
+    // static arg
+    eventName = createExpression(`on${capitalize(content)}`, true, argLoc)
+  } else if (!children) {
+    // dynamic arg with no rewrite
+    eventName = createExpression(
+      `"on" + ${isSimpleIdentifier(content) ? content : `(${content})`}`,
+      false,
+      argLoc
+    )
+  } else {
+    // dynamic arg with ctx prefixing
+    eventName = arg!
+    children.unshift(`"on" + `)
+  }
   // TODO .once modifier handling since it is platform agnostic
   // other modifiers are handled in compiler-dom
   return {
index 59704a2d633ae1dcf31963750b1a2456a4870272..f857f629edd48e13e2cb2b21cb4de56a434bb931 100644 (file)
@@ -1,5 +1,8 @@
 import { SourceLocation, Position } from './ast'
 
+export const isSimpleIdentifier = (name: string): boolean =>
+  !/^\d|[^\w]/.test(name)
+
 export function getInnerRange(
   loc: SourceLocation,
   offset: number,