From e640ec60880093eb8f65fcc1dfcb525b749523c9 Mon Sep 17 00:00:00 2001 From: Jevon <1787176370@qq.com> Date: Tue, 16 Apr 2024 16:46:22 +0800 Subject: [PATCH] fix(compiler-vapor): v-on for component support `$event` argument (#177) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 三咲智子 Kevin Deng --- .../transformElement.spec.ts.snap | 11 ++++ .../transforms/transformElement.spec.ts | 23 +++++++ .../src/generators/component.ts | 8 ++- .../compiler-vapor/src/generators/event.ts | 61 +++++++++++-------- .../compiler-vapor/src/generators/prop.ts | 7 ++- packages/compiler-vapor/src/transform.ts | 2 +- packages/compiler-vapor/src/transforms/vOn.ts | 7 +-- 7 files changed, 80 insertions(+), 39 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap index ccecacce1..55f92261e 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap @@ -91,6 +91,17 @@ export function render(_ctx) { }" `; +exports[`compiler: element transform > component > should wrap as function if v-on expression is inline statement 1`] = ` +"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; + +export function render(_ctx) { + const n0 = _createComponent(_resolveComponent("Foo"), [{ + onBar: () => $event => (_ctx.handleBar($event)) + }], true) + return n0 +}" +`; + exports[`compiler: element transform > component > static props 1`] = ` "import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor'; diff --git a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts index 9b0a27c96..6f80da227 100644 --- a/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts @@ -364,6 +364,29 @@ describe('compiler: element transform', () => { }, ]) }) + + test('should wrap as function if v-on expression is inline statement', () => { + const { code, ir } = compileWithElementTransform( + ``, + ) + expect(code).toMatchSnapshot() + expect(code).contains(`onBar: () => $event => (_ctx.handleBar($event))`) + expect(ir.block.operation).toMatchObject([ + { + type: IRNodeTypes.CREATE_COMPONENT_NODE, + tag: 'Foo', + props: [ + [ + { + key: { content: 'bar' }, + handler: true, + values: [{ content: 'handleBar($event)' }], + }, + ], + ], + }, + ]) + }) }) test('static props', () => { diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 1079aee38..ec9b9064e 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -12,6 +12,7 @@ import { import { genExpression } from './expression' import { genPropKey } from './prop' import { createSimpleExpression } from '@vue/compiler-dom' +import { genEventHandler } from './event' // TODO: generate component slots export function genCreateComponent( @@ -74,9 +75,10 @@ export function genCreateComponent( ...props.map(prop => { return [ ...genPropKey(prop, context), - ': () => (', - ...genExpression(prop.values[0], context), - ')', + ': ', + ...(prop.handler + ? genEventHandler(context, prop.values[0]) + : ['() => (', ...genExpression(prop.values[0], context), ')']), ] }), ) diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index df6738de2..e135410d1 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/packages/compiler-vapor/src/generators/event.ts @@ -1,4 +1,8 @@ -import { fnExpRE, isMemberExpression } from '@vue/compiler-dom' +import { + type SimpleExpressionNode, + fnExpRE, + isMemberExpression, +} from '@vue/compiler-dom' import type { CodegenContext } from '../generate' import type { SetDynamicEventsIRNode, SetEventIRNode } from '../ir' import { genExpression } from './expression' @@ -15,11 +19,11 @@ export function genSetEvent( oper: SetEventIRNode, context: CodegenContext, ): CodeFragment[] { - const { vaporHelper, options } = context + const { vaporHelper } = context const { element, key, keyOverride, value, modifiers, delegate, effect } = oper const name = genName() - const handler = genEventHandler() + const handler = genEventHandler(context, value) const eventOptions = genEventOptions() if (delegate) { @@ -51,30 +55,6 @@ export function genSetEvent( } } - function genEventHandler() { - if (value && value.content.trim()) { - const isMemberExp = isMemberExpression(value.content, options) - const isInlineStatement = !(isMemberExp || fnExpRE.test(value.content)) - - if (isInlineStatement) { - const expr = context.withId(() => genExpression(value, context), { - $event: null, - }) - const hasMultipleStatements = value.content.includes(`;`) - return [ - '() => $event => ', - hasMultipleStatements ? '{' : '(', - ...expr, - hasMultipleStatements ? '}' : ')', - ] - } else { - return ['() => ', ...genExpression(value, context)] - } - } - - return ['() => {}'] - } - function genEventOptions(): CodeFragment[] | undefined { let { options, keys, nonKeys } = modifiers if (!options.length && !nonKeys.length && !keys.length && !effect) return @@ -111,3 +91,30 @@ export function genSetDynamicEvents( function genArrayExpression(elements: string[]) { return `[${elements.map(it => JSON.stringify(it)).join(', ')}]` } + +export function genEventHandler( + context: CodegenContext, + value: SimpleExpressionNode | undefined, +) { + if (value && value.content.trim()) { + const isMemberExp = isMemberExpression(value.content, context.options) + const isInlineStatement = !(isMemberExp || fnExpRE.test(value.content)) + + if (isInlineStatement) { + const expr = context.withId(() => genExpression(value, context), { + $event: null, + }) + const hasMultipleStatements = value.content.includes(`;`) + return [ + '() => $event => ', + hasMultipleStatements ? '{' : '(', + ...expr, + hasMultipleStatements ? '}' : ')', + ] + } else { + return ['() => ', ...genExpression(value, context)] + } + } + + return ['() => {}'] +} diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index fb72c1d31..f6ea045b1 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -12,6 +12,7 @@ import type { } from '../ir' import { genExpression } from './expression' import { type CodeFragment, NEWLINE, genCall, genMulti } from './utils' +import { toHandlerKey } from '@vue/shared' // only the static key prop will reach here export function genSetProp( @@ -86,7 +87,7 @@ function genLiteralObjectProps( } export function genPropKey( - { key: node, modifier, runtimeCamelize, runtimeHandler }: IRProp, + { key: node, modifier, runtimeCamelize, handler }: IRProp, context: CodegenContext, ): CodeFragment[] { const { helper } = context @@ -94,7 +95,7 @@ export function genPropKey( // static arg was transformed by v-bind transformer if (node.isStatic) { // only quote keys if necessary - const keyName = node.content + const keyName = handler ? toHandlerKey(node.content) : node.content return [ [ isSimpleIdentifier(keyName) ? keyName : JSON.stringify(keyName), @@ -108,7 +109,7 @@ export function genPropKey( if (runtimeCamelize) { key = genCall(helper('camelize'), key) } - if (runtimeHandler) { + if (handler) { key = genCall(helper('toHandlerKey'), key) } return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']'] diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index b88b01055..5925e1d1f 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -43,7 +43,7 @@ export interface DirectiveTransformResult { value: SimpleExpressionNode modifier?: '.' | '^' runtimeCamelize?: boolean - runtimeHandler?: boolean + handler?: boolean } // A structural directive transform is technically also a NodeTransform; diff --git a/packages/compiler-vapor/src/transforms/vOn.ts b/packages/compiler-vapor/src/transforms/vOn.ts index cc098ec76..687bcfd65 100644 --- a/packages/compiler-vapor/src/transforms/vOn.ts +++ b/packages/compiler-vapor/src/transforms/vOn.ts @@ -6,7 +6,7 @@ import { import type { DirectiveTransform } from '../transform' import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir' import { resolveModifiers } from '@vue/compiler-dom' -import { extend, makeMap, toHandlerKey } from '@vue/shared' +import { extend, makeMap } from '@vue/shared' import { resolveExpression } from '../utils' import { EMPTY_EXPRESSION } from './utils' @@ -61,14 +61,11 @@ export const transformVOn: DirectiveTransform = (dir, node, context) => { } if (isComponent) { - if (arg.isStatic) { - arg = extend({}, arg, { content: toHandlerKey(arg.content) }) - } const handler = exp || EMPTY_EXPRESSION return { key: arg, value: handler, - runtimeHandler: !arg.isStatic, + handler: true, } }