Skip to content

Commit e7531d1

Browse files
committed
feat(compiler-vapor): component with dynamic arguments
1 parent e42fecb commit e7531d1

File tree

8 files changed

+194
-92
lines changed

8 files changed

+194
-92
lines changed

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

+29
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,35 @@ export function render(_ctx) {
179179
}"
180180
`;
181181

182+
exports[`compiler: element transform > component with dynamic event arguments 1`] = `
183+
"import { toHandlerKey as _toHandlerKey } from 'vue';
184+
import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
185+
186+
export function render(_ctx) {
187+
const _component_Foo = _resolveComponent("Foo")
188+
const n0 = _createComponent(_component_Foo, [() => ({
189+
[_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar
190+
}), () => ({
191+
[_toHandlerKey(_ctx.baz)]: () => _ctx.qux
192+
})], true)
193+
return n0
194+
}"
195+
`;
196+
197+
exports[`compiler: element transform > component with dynamic prop arguments 1`] = `
198+
"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
199+
200+
export function render(_ctx) {
201+
const _component_Foo = _resolveComponent("Foo")
202+
const n0 = _createComponent(_component_Foo, [() => ({
203+
[_ctx.foo-_ctx.bar]: _ctx.bar
204+
}), () => ({
205+
[_ctx.baz]: _ctx.qux
206+
})], true)
207+
return n0
208+
}"
209+
`;
210+
182211
exports[`compiler: element transform > invalid html nesting 1`] = `
183212
"import { insert as _insert, template as _template } from 'vue/vapor';
184213
const t0 = _template("<div>123</div>")

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

+9-8
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,15 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
6262
6363
export function render(_ctx) {
6464
const _component_Comp = _resolveComponent("Comp")
65-
const n0 = _createComponent(_component_Comp, [{
66-
[_ctx.foo]: () => (_ctx.foo),
65+
const n0 = _createComponent(_component_Comp, [() => ({
66+
[_ctx.foo]: _ctx.foo,
6767
["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event),
68-
[_ctx.foo + "Modifiers"]: () => ({ trim: true }),
69-
[_ctx.bar]: () => (_ctx.bar),
68+
[_ctx.foo + "Modifiers"]: () => ({ trim: true })
69+
}), () => ({
70+
[_ctx.bar]: _ctx.bar,
7071
["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event),
7172
[_ctx.bar + "Modifiers"]: () => ({ number: true })
72-
}], true)
73+
})], true)
7374
return n0
7475
}"
7576
`;
@@ -79,10 +80,10 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
7980
8081
export function render(_ctx) {
8182
const _component_Comp = _resolveComponent("Comp")
82-
const n0 = _createComponent(_component_Comp, [{
83-
[_ctx.arg]: () => (_ctx.foo),
83+
const n0 = _createComponent(_component_Comp, [() => ({
84+
[_ctx.arg]: _ctx.foo,
8485
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)
85-
}], true)
86+
})], true)
8687
return n0
8788
}"
8889
`;

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

+48
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,54 @@ describe('compiler: element transform', () => {
691691
expect(code).contains('_setDynamicEvents(n0, _ctx.obj)')
692692
})
693693

694+
test('component with dynamic prop arguments', () => {
695+
const { code, ir } = compileWithElementTransform(
696+
`<Foo :[foo-bar]="bar" :[baz]="qux" />`,
697+
)
698+
expect(code).toMatchSnapshot()
699+
expect(ir.block.operation).toMatchObject([
700+
{
701+
type: IRNodeTypes.CREATE_COMPONENT_NODE,
702+
tag: 'Foo',
703+
props: [
704+
{
705+
key: { content: 'foo-bar' },
706+
values: [{ content: 'bar' }],
707+
},
708+
{
709+
key: { content: 'baz' },
710+
values: [{ content: 'qux' }],
711+
},
712+
],
713+
},
714+
])
715+
})
716+
717+
test('component with dynamic event arguments', () => {
718+
const { code, ir } = compileWithElementTransform(
719+
`<Foo @[foo-bar]="bar" @[baz]="qux" />`,
720+
)
721+
expect(code).toMatchSnapshot()
722+
expect(ir.block.operation).toMatchObject([
723+
{
724+
type: IRNodeTypes.CREATE_COMPONENT_NODE,
725+
tag: 'Foo',
726+
props: [
727+
{
728+
key: { content: 'foo-bar' },
729+
values: [{ content: 'bar' }],
730+
handler: true,
731+
},
732+
{
733+
key: { content: 'baz' },
734+
values: [{ content: 'qux' }],
735+
handler: true,
736+
},
737+
],
738+
},
739+
])
740+
})
741+
694742
test('invalid html nesting', () => {
695743
const { code, ir } = compileWithElementTransform(
696744
`<p><div>123</div></p>

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

+19-23
Original file line numberDiff line numberDiff line change
@@ -259,22 +259,20 @@ describe('compiler: vModel transform', () => {
259259
const { code, ir } = compileWithVModel('<Comp v-model:[arg]="foo" />')
260260
expect(code).toMatchSnapshot()
261261
expect(code).contains(
262-
`[_ctx.arg]: () => (_ctx.foo),
262+
`[_ctx.arg]: _ctx.foo,
263263
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)`,
264264
)
265265
expect(ir.block.operation).toMatchObject([
266266
{
267267
type: IRNodeTypes.CREATE_COMPONENT_NODE,
268268
tag: 'Comp',
269269
props: [
270-
[
271-
{
272-
key: { content: 'arg', isStatic: false },
273-
values: [{ content: 'foo', isStatic: false }],
274-
model: true,
275-
modelModifiers: [],
276-
},
277-
],
270+
{
271+
key: { content: 'arg', isStatic: false },
272+
values: [{ content: 'foo', isStatic: false }],
273+
model: true,
274+
modelModifiers: [],
275+
},
278276
],
279277
},
280278
])
@@ -349,20 +347,18 @@ describe('compiler: vModel transform', () => {
349347
type: IRNodeTypes.CREATE_COMPONENT_NODE,
350348
tag: 'Comp',
351349
props: [
352-
[
353-
{
354-
key: { content: 'foo', isStatic: false },
355-
values: [{ content: 'foo', isStatic: false }],
356-
model: true,
357-
modelModifiers: ['trim'],
358-
},
359-
{
360-
key: { content: 'bar', isStatic: false },
361-
values: [{ content: 'bar', isStatic: false }],
362-
model: true,
363-
modelModifiers: ['number'],
364-
},
365-
],
350+
{
351+
key: { content: 'foo', isStatic: false },
352+
values: [{ content: 'foo', isStatic: false }],
353+
model: true,
354+
modelModifiers: ['trim'],
355+
},
356+
{
357+
key: { content: 'bar', isStatic: false },
358+
values: [{ content: 'bar', isStatic: false }],
359+
model: true,
360+
modelModifiers: ['number'],
361+
},
366362
],
367363
},
368364
])
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { camelize, extend, isArray } from '@vue/shared'
22
import type { CodegenContext } from '../generate'
3-
import type { CreateComponentIRNode, IRProp } from '../ir'
3+
import type { CreateComponentIRNode, IRProp, IRProps } from '../ir'
44
import {
55
type CodeFragment,
66
NEWLINE,
@@ -21,11 +21,11 @@ export function genCreateComponent(
2121
oper: CreateComponentIRNode,
2222
context: CodegenContext,
2323
): CodeFragment[] {
24-
const { helper, vaporHelper } = context
24+
const { vaporHelper } = context
2525

2626
const tag = genTag()
2727
const isRoot = oper.root
28-
const rawProps = genRawProps()
28+
const rawProps = genRawProps(oper.props, context)
2929

3030
return [
3131
NEWLINE,
@@ -49,63 +49,83 @@ export function genCreateComponent(
4949
)
5050
}
5151
}
52+
}
5253

53-
function genRawProps() {
54-
const props = oper.props
55-
.map(props => {
56-
if (isArray(props)) {
57-
if (!props.length) return
58-
return genStaticProps(props)
59-
} else {
60-
let expr = genExpression(props.value, context)
61-
if (props.handler) expr = genCall(helper('toHandlers'), expr)
62-
return ['() => (', ...expr, ')']
54+
export function genRawProps(props: IRProps[], context: CodegenContext) {
55+
const frag = props
56+
.map(props => {
57+
if (isArray(props)) {
58+
if (!props.length) return
59+
return genStaticProps(props, context)
60+
} else {
61+
let expr: CodeFragment[]
62+
if ('key' in props)
63+
expr = genMulti(
64+
SEGMENTS_OBJECT_NEWLINE,
65+
genProp(props, context, false),
66+
)
67+
else {
68+
expr = genExpression(props.value, context)
69+
if (props.handler) expr = genCall(context.helper('toHandlers'), expr)
6370
}
64-
})
65-
.filter(Boolean)
66-
if (props.length) {
67-
return genMulti(SEGMENTS_ARRAY, ...props)
68-
}
71+
return ['() => (', ...expr, ')']
72+
}
73+
})
74+
.filter(
75+
Boolean as any as (v: CodeFragment[] | undefined) => v is CodeFragment[],
76+
)
77+
if (frag.length) {
78+
return genMulti(SEGMENTS_ARRAY, ...frag)
6979
}
80+
}
7081

71-
function genStaticProps(props: IRProp[]) {
72-
return genMulti(
73-
SEGMENTS_OBJECT_NEWLINE,
74-
...props.map(prop => {
75-
return [
76-
...genPropKey(prop, context),
77-
': ',
78-
...(prop.handler
79-
? genEventHandler(context, prop.values[0])
80-
: ['() => (', ...genExpression(prop.values[0], context), ')']),
81-
...(prop.model
82-
? [...genModelEvent(prop), ...genModelModifiers(prop)]
83-
: []),
84-
]
85-
}),
86-
)
82+
function genStaticProps(
83+
props: IRProp[],
84+
context: CodegenContext,
85+
): CodeFragment[] {
86+
return genMulti(
87+
SEGMENTS_OBJECT_NEWLINE,
88+
...props.map(prop => genProp(prop, context, true)),
89+
)
90+
}
91+
92+
function genProp(prop: IRProp, context: CodegenContext, isStaticArg: boolean) {
93+
return [
94+
...genPropKey(prop, context),
95+
': ',
96+
...(prop.handler
97+
? genEventHandler(context, prop.values[0])
98+
: isStaticArg
99+
? ['() => (', ...genExpression(prop.values[0], context), ')']
100+
: genExpression(prop.values[0], context)),
101+
...(prop.model
102+
? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)]
103+
: []),
104+
]
105+
}
87106

88-
function genModelEvent(prop: IRProp): CodeFragment[] {
89-
const name = prop.key.isStatic
90-
? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)]
91-
: ['["onUpdate:" + ', ...genExpression(prop.key, context), ']']
92-
const handler = genModelHandler(prop.values[0], context)
107+
function genModelEvent(prop: IRProp, context: CodegenContext): CodeFragment[] {
108+
const name = prop.key.isStatic
109+
? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)]
110+
: ['["onUpdate:" + ', ...genExpression(prop.key, context), ']']
111+
const handler = genModelHandler(prop.values[0], context)
93112

94-
return [',', NEWLINE, ...name, ': ', ...handler]
95-
}
113+
return [',', NEWLINE, ...name, ': ', ...handler]
114+
}
96115

97-
function genModelModifiers(prop: IRProp): CodeFragment[] {
98-
const { key, modelModifiers } = prop
99-
if (!modelModifiers || !modelModifiers.length) return []
116+
function genModelModifiers(
117+
prop: IRProp,
118+
context: CodegenContext,
119+
): CodeFragment[] {
120+
const { key, modelModifiers } = prop
121+
if (!modelModifiers || !modelModifiers.length) return []
100122

101-
const modifiersKey = key.isStatic
102-
? key.content === 'modelValue'
103-
? [`modelModifiers`]
104-
: [`${key.content}Modifiers`]
105-
: ['[', ...genExpression(key, context), ' + "Modifiers"]']
123+
const modifiersKey = key.isStatic
124+
? key.content === 'modelValue'
125+
? [`modelModifiers`]
126+
: [`${key.content}Modifiers`]
127+
: ['[', ...genExpression(key, context), ' + "Modifiers"]']
106128

107-
const modifiersVal = genDirectiveModifiers(modelModifiers)
108-
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
109-
}
110-
}
129+
const modifiersVal = genDirectiveModifiers(modelModifiers)
130+
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
111131
}

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ export function genDynamicProps(
7373
props =>
7474
Array.isArray(props)
7575
? genLiteralObjectProps(props, context) // static and dynamic arg props
76-
: genExpression(props.value, context), // v-bind=""
76+
: 'key' in props
77+
? genLiteralObjectProps([props], context) // dynamic arg props
78+
: genExpression(props.value, context), // v-bind=""
7779
),
7880
),
7981
]

packages/compiler-vapor/src/ir.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -83,12 +83,11 @@ export interface ForIRNode extends BaseIRNode {
8383
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
8484
values: SimpleExpressionNode[]
8585
}
86-
export type IRProps =
87-
| IRProp[]
88-
| {
89-
value: SimpleExpressionNode
90-
handler?: boolean
91-
}
86+
export interface IRDynamicProps {
87+
value: SimpleExpressionNode
88+
handler?: boolean
89+
}
90+
export type IRProps = IRProp[] | IRProp | IRDynamicProps
9291

9392
export interface SetPropIRNode extends BaseIRNode {
9493
type: IRNodeTypes.SET_PROP

packages/compiler-vapor/src/transforms/transformElement.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -241,8 +241,15 @@ function buildProps(
241241

242242
const result = transformProp(prop, node, context)
243243
if (result) {
244-
results.push(result)
245244
dynamicExpr.push(result.key, result.value)
245+
if (isComponent && !result.key.isStatic) {
246+
// v-bind:[name]="value" or v-on:[name]="value"
247+
pushMergeArg()
248+
dynamicArgs.push(normalizeIRProp(result))
249+
} else {
250+
// other static props
251+
results.push(result)
252+
}
246253
}
247254
}
248255

0 commit comments

Comments
 (0)