Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
8 changes: 5 additions & 3 deletions packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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), ')']),
]
}),
)
Expand Down
61 changes: 34 additions & 27 deletions packages/compiler-vapor/src/generators/event.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 ['() => {}']
}
7 changes: 4 additions & 3 deletions packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -86,15 +87,15 @@ function genLiteralObjectProps(
}

export function genPropKey(
{ key: node, modifier, runtimeCamelize, runtimeHandler }: IRProp,
{ key: node, modifier, runtimeCamelize, handler }: IRProp,
context: CodegenContext,
): CodeFragment[] {
const { helper } = context

// 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),
Expand All @@ -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, ']']
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-vapor/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 2 additions & 5 deletions packages/compiler-vapor/src/transforms/vOn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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,
}
}

Expand Down