]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler/v-on): handle multiple statements in v-on handler (close #572)
authorEvan You <yyx990803@gmail.com>
Mon, 6 Jan 2020 16:45:48 +0000 (11:45 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 6 Jan 2020 16:45:48 +0000 (11:45 -0500)
packages/compiler-core/__tests__/transforms/vOn.spec.ts
packages/compiler-core/src/transforms/transformExpression.ts
packages/compiler-core/src/transforms/vOn.ts

index 7dfe92a0026485d8b3f36b49dff9a82f2a4d2e6d..596d7352d093462d29bfc808843e69738b3fd792 100644 (file)
@@ -140,6 +140,22 @@ describe('compiler: transform v-on', () => {
     })
   })
 
+  test('should handle multiple inline statement', () => {
+    const { node } = parseWithVOn(`<div @click="foo();bar()"/>`)
+    const props = (node.codegenNode as CallExpression)
+      .arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: { content: `onClick` },
+      value: {
+        type: NodeTypes.COMPOUND_EXPRESSION,
+        // should wrap with `{` for multiple statements
+        // in this case the return value is discarded and the behavior is
+        // consistent with 2.x
+        children: [`$event => {`, { content: `foo();bar()` }, `}`]
+      }
+    })
+  })
+
   test('inline statement w/ prefixIdentifiers: true', () => {
     const { node } = parseWithVOn(`<div @click="foo($event)"/>`, {
       prefixIdentifiers: true
@@ -163,6 +179,31 @@ describe('compiler: transform v-on', () => {
     })
   })
 
+  test('multiple inline statements w/ prefixIdentifiers: true', () => {
+    const { node } = parseWithVOn(`<div @click="foo($event);bar()"/>`, {
+      prefixIdentifiers: true
+    })
+    const props = (node.codegenNode as CallExpression)
+      .arguments[1] as ObjectExpression
+    expect(props.properties[0]).toMatchObject({
+      key: { content: `onClick` },
+      value: {
+        type: NodeTypes.COMPOUND_EXPRESSION,
+        children: [
+          `$event => {`,
+          { content: `_ctx.foo` },
+          `(`,
+          // should NOT prefix $event
+          { content: `$event` },
+          `);`,
+          { content: `_ctx.bar` },
+          `()`,
+          `}`
+        ]
+      }
+    })
+  })
+
   test('should NOT wrap as function if expression is already function expression', () => {
     const { node } = parseWithVOn(`<div @click="$event => foo($event)"/>`)
     const props = (node.codegenNode as CallExpression)
index 6e10782e627f4a317658cf25153f9506f27d9c61..1dde9258fe0f28497ac7dafa7d4a848804ba5d98 100644 (file)
@@ -76,7 +76,9 @@ export function processExpression(
   context: TransformContext,
   // some expressions like v-slot props & v-for aliases should be parsed as
   // function params
-  asParams: boolean = false
+  asParams = false,
+  // v-on handler values may contain multiple statements
+  asRawStatements = false
 ): ExpressionNode {
   if (!context.prefixIdentifiers || !node.content.trim()) {
     return node
@@ -100,9 +102,14 @@ export function processExpression(
   }
 
   let ast: any
-  // if the expression is supposed to be used in a function params position
-  // we need to parse it differently.
-  const source = `(${rawExp})${asParams ? `=>{}` : ``}`
+  // exp needs to be parsed differently:
+  // 1. Multiple inline statements (v-on, with presence of `;`): parse as raw
+  //    exp, but make sure to pad with spaces for consistent ranges
+  // 2. Expressions: wrap with parens (for e.g. object expressions)
+  // 3. Function arguments (v-for, v-slot): place in a function argument position
+  const source = asRawStatements
+    ? ` ${rawExp} `
+    : `(${rawExp})${asParams ? `=>{}` : ``}`
   try {
     ast = parseJS(source, { ranges: true })
   } catch (e) {
index ae56e0b9825ebd7a72e44d0b9cbd7db3d6da75d5..a43f5cbe8583f4d4a4703714a95c7b5a553fa860 100644 (file)
@@ -59,11 +59,12 @@ export const transformOn: DirectiveTransform = (
   if (exp) {
     const isMemberExp = isMemberExpression(exp.content)
     const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
+    const hasMultipleStatements = exp.content.includes(`;`)
 
     // process the expression since it's been skipped
     if (!__BROWSER__ && context.prefixIdentifiers) {
       context.addIdentifiers(`$event`)
-      exp = processExpression(exp, context)
+      exp = processExpression(exp, context, false, hasMultipleStatements)
       context.removeIdentifiers(`$event`)
       // with scope analysis, the function is hoistable if it has no reference
       // to scope variables.
@@ -85,9 +86,9 @@ export const transformOn: DirectiveTransform = (
     if (isInlineStatement || (isCacheable && isMemberExp)) {
       // wrap inline statement in a function expression
       exp = createCompoundExpression([
-        `$event => (`,
+        `$event => ${hasMultipleStatements ? `{` : `(`}`,
         ...(exp.type === NodeTypes.SIMPLE_EXPRESSION ? [exp] : exp.children),
-        `)`
+        hasMultipleStatements ? `}` : `)`
       ])
     }
   }