Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.

Commit 9e0cd20

Browse files
committed
feat(compiler-vapor): support v-on for component
1 parent 3787a43 commit 9e0cd20

File tree

8 files changed

+107
-163
lines changed

8 files changed

+107
-163
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,6 @@ export function render(_ctx) {
3636
}"
3737
`;
3838

39-
exports[`compiler: element transform > component > props merging: event handlers 1`] = `
40-
"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
41-
42-
export function render(_ctx) {
43-
const n0 = _createComponent(_resolveComponent("Foo"), [{
44-
onClick: () => (_ctx.a)
45-
}], true)
46-
return n0
47-
}"
48-
`;
49-
5039
exports[`compiler: element transform > component > resolve component from setup bindings (inline const) 1`] = `
5140
"(() => {
5241
const n0 = _createComponent(Example, null, true)
@@ -158,6 +147,16 @@ export function render(_ctx) {
158147
}"
159148
`;
160149

150+
exports[`compiler: element transform > component > v-on="obj" 1`] = `
151+
"import { toHandlers as _toHandlers } from 'vue';
152+
import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
153+
154+
export function render(_ctx) {
155+
const n0 = _createComponent(_resolveComponent("Foo"), [() => (_toHandlers(_ctx.obj))], true)
156+
return n0
157+
}"
158+
`;
159+
161160
exports[`compiler: element transform > props + children 1`] = `
162161
"import { template as _template } from 'vue/vapor';
163162
const t0 = _template("<div id=\\"foo\\"><span></span></div>")

packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts

Lines changed: 35 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ describe('compiler: element transform', () => {
252252
{
253253
type: IRNodeTypes.CREATE_COMPONENT_NODE,
254254
tag: 'Foo',
255-
props: [{ content: 'obj', isStatic: false }],
255+
props: [{ value: { content: 'obj', isStatic: false } }],
256256
},
257257
])
258258
})
@@ -270,7 +270,7 @@ describe('compiler: element transform', () => {
270270
tag: 'Foo',
271271
props: [
272272
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
273-
{ content: 'obj' },
273+
{ value: { content: 'obj' } },
274274
],
275275
},
276276
])
@@ -288,7 +288,7 @@ describe('compiler: element transform', () => {
288288
type: IRNodeTypes.CREATE_COMPONENT_NODE,
289289
tag: 'Foo',
290290
props: [
291-
{ content: 'obj' },
291+
{ value: { content: 'obj' } },
292292
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
293293
],
294294
},
@@ -309,19 +309,19 @@ describe('compiler: element transform', () => {
309309
tag: 'Foo',
310310
props: [
311311
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
312-
{ content: 'obj' },
312+
{ value: { content: 'obj' } },
313313
[{ key: { content: 'class' }, values: [{ content: 'bar' }] }],
314314
],
315315
},
316316
])
317317
})
318318

319-
test('props merging: event handlers', () => {
319+
test.todo('props merging: event handlers', () => {
320320
const { code, ir } = compileWithElementTransform(
321321
`<Foo @click.foo="a" @click.bar="b" />`,
322322
)
323323
expect(code).toMatchSnapshot()
324-
expect(code).contains('onClick: () => (_ctx.a)')
324+
expect(code).contains('onClick: () => [_ctx.a, _ctx.b]')
325325
expect(ir.block.operation).toMatchObject([
326326
{
327327
type: IRNodeTypes.CREATE_COMPONENT_NODE,
@@ -330,7 +330,7 @@ describe('compiler: element transform', () => {
330330
[
331331
{
332332
key: { content: 'onClick', isStatic: true },
333-
values: [{ content: 'a' }],
333+
values: [{ content: 'a' }, { content: 'b' }],
334334
},
335335
],
336336
],
@@ -352,9 +352,17 @@ describe('compiler: element transform', () => {
352352
expect(code).toMatchSnapshot()
353353
})
354354

355-
test.todo('v-on="obj"', () => {
356-
const { code } = compileWithElementTransform(`<Foo v-on="obj" />`)
355+
test('v-on="obj"', () => {
356+
const { code, ir } = compileWithElementTransform(`<Foo v-on="obj" />`)
357357
expect(code).toMatchSnapshot()
358+
expect(code).contains('[() => (_toHandlers(_ctx.obj))]')
359+
expect(ir.block.operation).toMatchObject([
360+
{
361+
type: IRNodeTypes.CREATE_COMPONENT_NODE,
362+
tag: 'Foo',
363+
props: [{ value: { content: 'obj' }, handler: true }],
364+
},
365+
])
358366
})
359367
})
360368

@@ -400,9 +408,11 @@ describe('compiler: element transform', () => {
400408
element: 0,
401409
props: [
402410
{
403-
type: 4,
404-
content: 'obj',
405-
isStatic: false,
411+
value: {
412+
type: NodeTypes.SIMPLE_EXPRESSION,
413+
content: 'obj',
414+
isStatic: false,
415+
},
406416
},
407417
],
408418
},
@@ -431,26 +441,13 @@ describe('compiler: element transform', () => {
431441
type: IRNodeTypes.SET_DYNAMIC_PROPS,
432442
element: 0,
433443
props: [
434-
[
435-
{
436-
key: {
437-
type: NodeTypes.SIMPLE_EXPRESSION,
438-
content: 'id',
439-
isStatic: true,
440-
},
441-
values: [
442-
{
443-
type: NodeTypes.SIMPLE_EXPRESSION,
444-
content: 'foo',
445-
isStatic: true,
446-
},
447-
],
448-
},
449-
],
444+
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
450445
{
451-
type: NodeTypes.SIMPLE_EXPRESSION,
452-
content: 'obj',
453-
isStatic: false,
446+
value: {
447+
type: NodeTypes.SIMPLE_EXPRESSION,
448+
content: 'obj',
449+
isStatic: false,
450+
},
454451
},
455452
],
456453
},
@@ -467,39 +464,14 @@ describe('compiler: element transform', () => {
467464
expect(code).toMatchSnapshot()
468465
expect(ir.block.effect).toMatchObject([
469466
{
470-
expressions: [
471-
{
472-
type: NodeTypes.SIMPLE_EXPRESSION,
473-
content: 'obj',
474-
isStatic: false,
475-
},
476-
],
467+
expressions: [{ content: 'obj' }],
477468
operations: [
478469
{
479470
type: IRNodeTypes.SET_DYNAMIC_PROPS,
480471
element: 0,
481472
props: [
482-
{
483-
type: NodeTypes.SIMPLE_EXPRESSION,
484-
content: 'obj',
485-
isStatic: false,
486-
},
487-
[
488-
{
489-
key: {
490-
type: NodeTypes.SIMPLE_EXPRESSION,
491-
content: 'id',
492-
isStatic: true,
493-
},
494-
values: [
495-
{
496-
type: NodeTypes.SIMPLE_EXPRESSION,
497-
content: 'foo',
498-
isStatic: true,
499-
},
500-
],
501-
},
502-
],
473+
{ value: { content: 'obj' } },
474+
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
503475
],
504476
},
505477
],
@@ -515,55 +487,15 @@ describe('compiler: element transform', () => {
515487
expect(code).toMatchSnapshot()
516488
expect(ir.block.effect).toMatchObject([
517489
{
518-
expressions: [
519-
{
520-
type: NodeTypes.SIMPLE_EXPRESSION,
521-
content: 'obj',
522-
isStatic: false,
523-
},
524-
],
490+
expressions: [{ content: 'obj' }],
525491
operations: [
526492
{
527493
type: IRNodeTypes.SET_DYNAMIC_PROPS,
528494
element: 0,
529495
props: [
530-
[
531-
{
532-
key: {
533-
type: NodeTypes.SIMPLE_EXPRESSION,
534-
content: 'id',
535-
isStatic: true,
536-
},
537-
values: [
538-
{
539-
type: NodeTypes.SIMPLE_EXPRESSION,
540-
content: 'foo',
541-
isStatic: true,
542-
},
543-
],
544-
},
545-
],
546-
{
547-
type: NodeTypes.SIMPLE_EXPRESSION,
548-
content: 'obj',
549-
isStatic: false,
550-
},
551-
[
552-
{
553-
key: {
554-
type: NodeTypes.SIMPLE_EXPRESSION,
555-
content: 'class',
556-
isStatic: true,
557-
},
558-
values: [
559-
{
560-
type: NodeTypes.SIMPLE_EXPRESSION,
561-
content: 'bar',
562-
isStatic: true,
563-
},
564-
],
565-
},
566-
],
496+
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
497+
{ value: { content: 'obj' } },
498+
[{ key: { content: 'class' }, values: [{ content: 'bar' }] }],
567499
],
568500
},
569501
],

packages/compiler-vapor/__tests__/transforms/vModel.spec.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,71 +14,68 @@ describe('compiler: vModel transform', () => {
1414
const { code, vaporHelpers } = compileWithVModel(
1515
'<input v-model="model" />',
1616
)
17-
18-
expect(vaporHelpers).toContain('vModelText')
1917
expect(code).toMatchSnapshot()
18+
expect(vaporHelpers).toContain('vModelText')
2019
})
2120

2221
test('should support input (text)', () => {
2322
const { code, vaporHelpers } = compileWithVModel(
2423
'<input type="text" v-model="model" />',
2524
)
26-
expect(vaporHelpers).toContain('vModelText')
2725
expect(code).toMatchSnapshot()
26+
expect(vaporHelpers).toContain('vModelText')
2827
})
2928

3029
test('should support input (radio)', () => {
3130
const { code, vaporHelpers } = compileWithVModel(
3231
'<input type="radio" v-model="model" />',
3332
)
34-
expect(vaporHelpers).toContain('vModelRadio')
3533
expect(code).toMatchSnapshot()
34+
expect(vaporHelpers).toContain('vModelRadio')
3635
})
3736

3837
test('should support input (checkbox)', () => {
3938
const { code, vaporHelpers } = compileWithVModel(
4039
'<input type="checkbox" v-model="model" />',
4140
)
42-
expect(vaporHelpers).toContain('vModelCheckbox')
4341
expect(code).toMatchSnapshot()
42+
expect(vaporHelpers).toContain('vModelCheckbox')
4443
})
4544

4645
test('should support select', () => {
4746
const { code, vaporHelpers } = compileWithVModel(
4847
'<select v-model="model" />',
4948
)
50-
51-
expect(vaporHelpers).toContain('vModelSelect')
5249
expect(code).toMatchSnapshot()
50+
expect(vaporHelpers).toContain('vModelSelect')
5351
})
5452

5553
test('should support textarea', () => {
5654
const { code, vaporHelpers } = compileWithVModel(
5755
'<textarea v-model="model" />',
5856
)
59-
60-
expect(vaporHelpers).toContain('vModelText')
6157
expect(code).toMatchSnapshot()
58+
expect(vaporHelpers).toContain('vModelText')
6259
})
6360

6461
test('should support input (dynamic type)', () => {
6562
const { code, vaporHelpers } = compileWithVModel(
6663
'<input :type="foo" v-model="model" />',
6764
)
68-
expect(vaporHelpers).toContain('vModelDynamic')
6965
expect(code).toMatchSnapshot()
66+
expect(vaporHelpers).toContain('vModelDynamic')
7067
})
7168

7269
test('should support w/ dynamic v-bind', () => {
7370
const root1 = compileWithVModel('<input v-bind="obj" v-model="model" />')
74-
expect(root1.vaporHelpers).toContain('vModelDynamic')
7571
expect(root1.code).toMatchSnapshot()
72+
expect(root1.vaporHelpers).toContain('vModelDynamic')
7673

7774
const root2 = compileWithVModel(
7875
'<input v-bind:[key]="val" v-model="model" />',
7976
)
80-
expect(root2.vaporHelpers).toContain('vModelDynamic')
8177
expect(root2.code).toMatchSnapshot()
78+
expect(root2.vaporHelpers).toContain('vModelDynamic')
8279
})
8380

8481
describe('errors', () => {

packages/compiler-vapor/src/generators/component.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function genCreateComponent(
1818
oper: CreateComponentIRNode,
1919
context: CodegenContext,
2020
): CodeFragment[] {
21-
const { vaporHelper } = context
21+
const { helper, vaporHelper } = context
2222

2323
const tag = genTag()
2424
const isRoot = oper.root
@@ -53,7 +53,9 @@ export function genCreateComponent(
5353
if (!props.length) return undefined
5454
return genStaticProps(props)
5555
} else {
56-
return ['() => (', ...genExpression(props, context), ')']
56+
let expr = genExpression(props.value, context)
57+
if (props.handler) expr = genCall(helper('toHandlers'), expr)
58+
return ['() => (', ...expr, ')']
5759
}
5860
})
5961
.filter(Boolean)

packages/compiler-vapor/src/generators/prop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function genDynamicProps(
6565
props =>
6666
Array.isArray(props)
6767
? genLiteralObjectProps(props, context) // static and dynamic arg props
68-
: genExpression(props, context), // v-bind="{}"
68+
: genExpression(props.value, context), // v-bind=""
6969
),
7070
),
7171
]

packages/compiler-vapor/src/ir.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,12 @@ export interface ForIRNode extends BaseIRNode {
8282
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
8383
values: SimpleExpressionNode[]
8484
}
85-
export type IRProps = IRProp[] | SimpleExpressionNode
85+
export type IRProps =
86+
| IRProp[]
87+
| {
88+
value: SimpleExpressionNode
89+
handler?: boolean
90+
}
8691

8792
export interface SetPropIRNode extends BaseIRNode {
8893
type: IRNodeTypes.SET_PROP

0 commit comments

Comments
 (0)