refactor(compiler-vapor): v-on

This commit is contained in:
三咲智子 Kevin Deng 2023-12-10 00:06:20 +08:00
parent 45e86e36d7
commit da8e196ca5
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
4 changed files with 78 additions and 59 deletions

View File

@ -152,11 +152,17 @@ export const isMemberExpressionBrowser = (path: string): boolean => {
}
export const isMemberExpressionNode = __BROWSER__
? (NOOP as any as (path: string, context: TransformContext) => boolean)
: (path: string, context: TransformContext): boolean => {
? (NOOP as any as (
path: string,
options: Pick<TransformContext, 'expressionPlugins'>
) => boolean)
: (
path: string,
options: Pick<TransformContext, 'expressionPlugins'>
): boolean => {
try {
let ret: Expression = parseExpression(path, {
plugins: context.expressionPlugins
plugins: options.expressionPlugins
})
if (ret.type === 'TSAsExpression' || ret.type === 'TSTypeAssertion') {
ret = ret.expression

View File

@ -438,31 +438,48 @@ function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
const { vaporHelper, push, pushWithNewline } = context
pushWithNewline(`${vaporHelper('on')}(n${oper.element}, `)
// second arg: event name
genExpression(oper.key, context)
// 2nd arg: event name
if (oper.keyOverride) {
const find = JSON.stringify(oper.keyOverride[0])
const replacement = JSON.stringify(oper.keyOverride[1])
push('(')
genExpression(oper.key, context)
push(`) === ${find} ? ${replacement} : (`)
genExpression(oper.key, context)
push(')')
} else {
genExpression(oper.key, context)
}
push(', ')
const { keys, nonKeys, options } = oper.modifiers
if (keys.length) {
push(`${vaporHelper('withKeys')}(`)
}
if (nonKeys.length) {
push(`${vaporHelper('withModifiers')}(`)
// 3rd arg: event handler
if (oper.value && oper.value.content.trim()) {
if (keys.length) {
push(`${vaporHelper('withKeys')}(`)
}
if (nonKeys.length) {
push(`${vaporHelper('withModifiers')}(`)
}
push('(...args) => (')
genExpression(oper.value, context)
push(' && ')
genExpression(oper.value, context)
push('(...args))')
if (nonKeys.length) {
push(`, ${genArrayExpression(nonKeys)})`)
}
if (keys.length) {
push(`, ${genArrayExpression(keys)})`)
}
} else {
push('() => {}')
}
// gen event handler
push('(...args) => (')
genExpression(oper.value, context)
push(' && ')
genExpression(oper.value, context)
push('(...args))')
if (nonKeys.length) {
push(`, ${genArrayExpression(nonKeys)})`)
}
if (keys.length) {
push(`, ${genArrayExpression(keys)})`)
}
// 4th arg, gen options
if (options.length) {
push(`, { ${options.map((v) => `${v}: true`).join(', ')} }`)
}

View File

@ -69,11 +69,12 @@ export interface SetTextIRNode extends BaseIRNode {
value: IRExpression
}
export type KeyOverride = [find: string, replacement: string]
export interface SetEventIRNode extends BaseIRNode {
type: IRNodeTypes.SET_EVENT
element: number
key: IRExpression
value: IRExpression
value?: SimpleExpressionNode
modifiers: {
// modifiers for addEventListener() options, e.g. .passive & .capture
options: string[]
@ -82,6 +83,7 @@ export interface SetEventIRNode extends BaseIRNode {
// modifiers that needs runtime guards, withModifiers
nonKeys: string[]
}
keyOverride?: KeyOverride
}
export interface SetHtmlIRNode extends BaseIRNode {

View File

@ -1,70 +1,64 @@
import {
createCompilerError,
createSimpleExpression,
ErrorCodes,
ExpressionNode,
isStaticExp,
NodeTypes,
} from '@vue/compiler-core'
import { createCompilerError, ErrorCodes } from '@vue/compiler-core'
import type { DirectiveTransform } from '../transform'
import { IRNodeTypes } from '../ir'
import { IRNodeTypes, KeyOverride } from '../ir'
import { resolveModifiers } from '@vue/compiler-dom'
export const transformVOn: DirectiveTransform = (dir, node, context) => {
const { arg, exp, loc, modifiers } = dir
let { arg, exp, loc, modifiers } = dir
if (!exp && !modifiers.length) {
context.options.onError(
createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc),
)
return
}
if (!arg) {
// TODO support v-on="{}"
return
} else if (exp === undefined) {
// TODO X_V_ON_NO_EXPRESSION error
return
}
const handlerKey = `on${arg.content}`
const { keyModifiers, nonKeyModifiers, eventOptionModifiers } =
resolveModifiers(handlerKey, modifiers, null, loc)
resolveModifiers(
arg.isStatic ? `on${arg.content}` : arg,
modifiers,
null,
loc,
)
let keyOverride: KeyOverride | undefined
// normalize click.right and click.middle since they don't actually fire
let name = arg.content
const isStaticClick = arg.isStatic && arg.content.toLowerCase() === 'click'
if (nonKeyModifiers.includes('right')) {
name = transformClick(arg, 'contextmenu')
if (isStaticClick) {
arg = { ...arg, content: 'contextmenu' }
} else if (!arg.isStatic) {
keyOverride = ['click', 'contextmenu']
}
}
if (nonKeyModifiers.includes('middle')) {
name = transformClick(arg, 'mouseup')
if (keyOverride) {
// TODO error here
}
if (isStaticClick) {
arg = { ...arg, content: 'mouseup' }
} else if (!arg.isStatic) {
keyOverride = ['click', 'mouseup']
}
}
// TODO reactive
context.registerOperation({
type: IRNodeTypes.SET_EVENT,
loc,
element: context.reference(),
key: createSimpleExpression(name, true, arg.loc),
key: arg,
value: exp,
modifiers: {
keys: keyModifiers,
nonKeys: nonKeyModifiers,
options: eventOptionModifiers,
},
keyOverride,
})
}
function transformClick(key: ExpressionNode, event: string) {
const isStaticClick =
isStaticExp(key) && key.content.toLowerCase() === 'click'
if (isStaticClick) {
return event
} else if (key.type !== NodeTypes.SIMPLE_EXPRESSION) {
// TODO: handle CompoundExpression
return 'TODO'
} else {
return key.content.toLowerCase()
}
}