foo($event)"/>`)
+ const props = node.codegenNode!.arguments[1] as ObjectExpression
+ expect(props.properties[0]).toMatchObject({
+ key: { content: `onClick` },
+ value: {
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: `$event => foo($event)`
+ }
+ })
+ })
+
+ test('function expression w/ prefixIdentifiers: true', () => {
+ const node = parseWithVOn(`
foo(e)"/>`, {
+ prefixIdentifiers: true
+ })
+ const props = node.codegenNode!.arguments[1] as ObjectExpression
+ expect(props.properties[0]).toMatchObject({
+ key: { content: `onClick` },
+ value: {
+ type: NodeTypes.COMPOUND_EXPRESSION,
+ children: [
+ { content: `e` },
+ ` => `,
+ { content: `_ctx.foo` },
+ `(`,
+ { content: `e` },
+ `)`
+ ]
+ }
+ })
+ })
+
test('should error if no expression AND no modifier', () => {
const onError = jest.fn()
parseWithVOn(`
`, { onError })
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index 370d4f46..588d3e49 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -72,8 +72,8 @@ export interface TransformContext extends Required
{
replaceNode(node: TemplateChildNode): void
removeNode(node?: TemplateChildNode): void
onNodeRemoved: () => void
- addIdentifiers(exp: ExpressionNode): void
- removeIdentifiers(exp: ExpressionNode): void
+ addIdentifiers(exp: ExpressionNode | string): void
+ removeIdentifiers(exp: ExpressionNode | string): void
hoist(exp: JSChildNode): SimpleExpressionNode
}
@@ -152,7 +152,9 @@ function createTransformContext(
addIdentifiers(exp) {
// identifier tracking only happens in non-browser builds.
if (!__BROWSER__) {
- if (exp.identifiers) {
+ if (isString(exp)) {
+ addId(exp)
+ } else if (exp.identifiers) {
exp.identifiers.forEach(addId)
} else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
addId(exp.content)
@@ -161,7 +163,9 @@ function createTransformContext(
},
removeIdentifiers(exp) {
if (!__BROWSER__) {
- if (exp.identifiers) {
+ if (isString(exp)) {
+ removeId(exp)
+ } else if (exp.identifiers) {
exp.identifiers.forEach(removeId)
} else if (exp.type === NodeTypes.SIMPLE_EXPRESSION) {
removeId(exp.content)
diff --git a/packages/compiler-core/src/transforms/transformExpression.ts b/packages/compiler-core/src/transforms/transformExpression.ts
index 36c37833..a88b28a9 100644
--- a/packages/compiler-core/src/transforms/transformExpression.ts
+++ b/packages/compiler-core/src/transforms/transformExpression.ts
@@ -35,11 +35,13 @@ export const transformExpression: NodeTransform = (node, context) => {
// handle directives on element
for (let i = 0; i < node.props.length; i++) {
const dir = node.props[i]
- // do not process for v-for since it's special handled
+ // do not process for v-on & v-for since they are special handled
if (dir.type === NodeTypes.DIRECTIVE && dir.name !== 'for') {
const exp = dir.exp as SimpleExpressionNode | undefined
const arg = dir.arg as SimpleExpressionNode | undefined
- if (exp) {
+ // do not process exp if this is v-on:arg - we need special handling
+ // for wrapping inline statements.
+ if (exp && !(dir.name === 'on' && arg)) {
dir.exp = processExpression(
exp,
context,
diff --git a/packages/compiler-core/src/transforms/vOn.ts b/packages/compiler-core/src/transforms/vOn.ts
index 2c89038f..a28ea544 100644
--- a/packages/compiler-core/src/transforms/vOn.ts
+++ b/packages/compiler-core/src/transforms/vOn.ts
@@ -4,18 +4,23 @@ import {
createSimpleExpression,
ExpressionNode,
NodeTypes,
- createCompoundExpression
+ createCompoundExpression,
+ SimpleExpressionNode
} from '../ast'
import { capitalize } from '@vue/shared'
import { createCompilerError, ErrorCodes } from '../errors'
+import { processExpression } from './transformExpression'
+
+const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function(?:\s+[\w$]+)?\s*\(/
+const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
// 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 = (dir, context) => {
- const { exp, loc, modifiers } = dir
+ const { loc, modifiers } = dir
const arg = dir.arg!
- if (!exp && !modifiers.length) {
+ if (!dir.exp && !modifiers.length) {
context.onError(createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc))
}
let eventName: ExpressionNode
@@ -37,10 +42,36 @@ export const transformOn: DirectiveTransform = (dir, context) => {
}
// TODO .once modifier handling since it is platform agnostic
// other modifiers are handled in compiler-dom
+
+ // handler processing
+ if (dir.exp) {
+ // exp is guarunteed to be a simple expression here because v-on w/ arg is
+ // skipped by transformExpression as a special case.
+ let exp: ExpressionNode = dir.exp as SimpleExpressionNode
+ const isInlineStatement = !(
+ simplePathRE.test(exp.content) || fnExpRE.test(exp.content)
+ )
+ // process the expression since it's been skipped
+ if (!__BROWSER__ && context.prefixIdentifiers) {
+ context.addIdentifiers(`$event`)
+ exp = processExpression(exp, context)
+ context.removeIdentifiers(`$event`)
+ }
+ if (isInlineStatement) {
+ // wrap inline statement in a function expression
+ exp = createCompoundExpression([
+ `$event => (`,
+ ...(exp.type === NodeTypes.SIMPLE_EXPRESSION ? [exp] : exp.children),
+ `)`
+ ])
+ }
+ dir.exp = exp
+ }
+
return {
props: createObjectProperty(
eventName,
- exp || createSimpleExpression(`() => {}`, false, loc)
+ dir.exp || createSimpleExpression(`() => {}`, false, loc)
),
needRuntime: false
}