feat(compiler-vapor): support v-on for component (#175)

Co-authored-by: Kevin Deng 三咲智子 <sxzz@sxzz.moe>
This commit is contained in:
Doctor Wu 2024-04-15 02:40:59 +08:00 committed by GitHub
parent bdc43226a0
commit a49b6f91ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 54 additions and 10 deletions

View File

@ -1,5 +1,20 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`generate component > generate component with emits 1`] = `
"import { toHandlerKey as _toHandlerKey } from 'vue';
import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
export function render(_ctx) {
const n0 = _createComponent(_resolveComponent("Comp"), [{
onClick: () => (fn)
}])
const n1 = _createComponent(_resolveComponent("Comp"), [{
[_toHandlerKey(eventName)]: () => (fn)
}])
return [n0, n1]
}"
`;
exports[`generate component > generate multi root component 1`] = ` exports[`generate component > generate multi root component 1`] = `
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor'; "import { resolveComponent as _resolveComponent, createComponent as _createComponent, template as _template } from 'vue/vapor';
const t0 = _template("123") const t0 = _template("123")

View File

@ -20,4 +20,12 @@ describe('generate component', () => {
const { code } = compile(`<div><Comp/></div>`) const { code } = compile(`<div><Comp/></div>`)
expect(code).toMatchSnapshot() expect(code).toMatchSnapshot()
}) })
test('generate component with emits', () => {
const { code } = compile(`
<Comp @click="fn" />
<Comp @[eventName]="fn" />
`)
expect(code).toMatchSnapshot()
})
}) })

View File

@ -86,7 +86,7 @@ function genLiteralObjectProps(
} }
export function genPropKey( export function genPropKey(
{ key: node, runtimeCamelize, modifier }: IRProp, { key: node, modifier, runtimeCamelize, runtimeHandler }: IRProp,
context: CodegenContext, context: CodegenContext,
): CodeFragment[] { ): CodeFragment[] {
const { helper } = context const { helper } = context
@ -104,13 +104,14 @@ export function genPropKey(
] ]
} }
const key = genExpression(node, context) let key = genExpression(node, context)
return [ if (runtimeCamelize) {
'[', key = genCall(helper('camelize'), key)
modifier && `${JSON.stringify(modifier)} + `, }
...(runtimeCamelize ? genCall(helper('camelize'), key) : key), if (runtimeHandler) {
']', key = genCall(helper('toHandlerKey'), key)
] }
return ['[', modifier && `${JSON.stringify(modifier)} + `, ...key, ']']
} }
function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) { function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) {

View File

@ -43,6 +43,7 @@ export interface DirectiveTransformResult {
value: SimpleExpressionNode value: SimpleExpressionNode
modifier?: '.' | '^' modifier?: '.' | '^'
runtimeCamelize?: boolean runtimeCamelize?: boolean
runtimeHandler?: boolean
} }
// A structural directive transform is technically also a NodeTransform; // A structural directive transform is technically also a NodeTransform;

View File

@ -1,9 +1,14 @@
import { ErrorCodes, createCompilerError } from '@vue/compiler-dom' import {
ElementTypes,
ErrorCodes,
createCompilerError,
} from '@vue/compiler-dom'
import type { DirectiveTransform } from '../transform' import type { DirectiveTransform } from '../transform'
import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir' import { IRNodeTypes, type KeyOverride, type SetEventIRNode } from '../ir'
import { resolveModifiers } from '@vue/compiler-dom' import { resolveModifiers } from '@vue/compiler-dom'
import { extend, makeMap } from '@vue/shared' import { extend, makeMap, toHandlerKey } from '@vue/shared'
import { resolveExpression } from '../utils' import { resolveExpression } from '../utils'
import { EMPTY_EXPRESSION } from './utils'
const delegatedEvents = /*#__PURE__*/ makeMap( const delegatedEvents = /*#__PURE__*/ makeMap(
'beforeinput,click,dblclick,contextmenu,focusin,focusout,input,keydown,' + 'beforeinput,click,dblclick,contextmenu,focusin,focusout,input,keydown,' +
@ -14,6 +19,8 @@ const delegatedEvents = /*#__PURE__*/ makeMap(
export const transformVOn: DirectiveTransform = (dir, node, context) => { export const transformVOn: DirectiveTransform = (dir, node, context) => {
let { arg, exp, loc, modifiers } = dir let { arg, exp, loc, modifiers } = dir
const isComponent = node.tagType === ElementTypes.COMPONENT
if (!exp && (!modifiers.length || !arg)) { if (!exp && (!modifiers.length || !arg)) {
context.options.onError( context.options.onError(
createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc), createCompilerError(ErrorCodes.X_V_ON_NO_EXPRESSION, loc),
@ -70,6 +77,18 @@ 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,
}
}
const operation: SetEventIRNode = { const operation: SetEventIRNode = {
type: IRNodeTypes.SET_EVENT, type: IRNodeTypes.SET_EVENT,
element: context.reference(), element: context.reference(),