fix(compiler-vapor): v-on for component support `$event` argument (#177)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
Jevon 2024-04-16 16:46:22 +08:00 committed by GitHub
parent 9e0cd20da0
commit e640ec6088
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 80 additions and 39 deletions

View File

@ -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';

View File

@ -364,6 +364,29 @@ describe('compiler: element transform', () => {
},
])
})
test('should wrap as function if v-on expression is inline statement', () => {
const { code, ir } = compileWithElementTransform(
`<Foo v-on:bar="handleBar($event)" />`,
)
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', () => {

View File

@ -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), ')']),
]
}),
)

View File

@ -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 ['() => {}']
}

View File

@ -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, ']']

View File

@ -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;

View File

@ -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,
}
}