Skip to content

Commit

Permalink
feat(compiler-vapor): component with dynamic arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleSound committed Apr 29, 2024
1 parent e42fecb commit e7531d1
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,35 @@ export function render(_ctx) {
}"
`;

exports[`compiler: element transform > component with dynamic event arguments 1`] = `
"import { toHandlerKey as _toHandlerKey } from 'vue';
import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [() => ({
[_toHandlerKey(_ctx.foo-_ctx.bar)]: () => _ctx.bar
}), () => ({
[_toHandlerKey(_ctx.baz)]: () => _ctx.qux
})], true)
return n0
}"
`;

exports[`compiler: element transform > component with dynamic prop arguments 1`] = `
"import { resolveComponent as _resolveComponent, createComponent as _createComponent } from 'vue/vapor';
export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [() => ({
[_ctx.foo-_ctx.bar]: _ctx.bar
}), () => ({
[_ctx.baz]: _ctx.qux
})], true)
return n0
}"
`;

exports[`compiler: element transform > invalid html nesting 1`] = `
"import { insert as _insert, template as _template } from 'vue/vapor';
const t0 = _template("<div>123</div>")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,15 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
[_ctx.foo]: () => (_ctx.foo),
const n0 = _createComponent(_component_Comp, [() => ({
[_ctx.foo]: _ctx.foo,
["onUpdate:" + _ctx.foo]: () => $event => (_ctx.foo = $event),
[_ctx.foo + "Modifiers"]: () => ({ trim: true }),
[_ctx.bar]: () => (_ctx.bar),
[_ctx.foo + "Modifiers"]: () => ({ trim: true })
}), () => ({
[_ctx.bar]: _ctx.bar,
["onUpdate:" + _ctx.bar]: () => $event => (_ctx.bar = $event),
[_ctx.bar + "Modifiers"]: () => ({ number: true })
}], true)
})], true)
return n0
}"
`;
Expand All @@ -79,10 +80,10 @@ exports[`compiler: vModel transform > component > v-model with dynamic arguments
export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
[_ctx.arg]: () => (_ctx.foo),
const n0 = _createComponent(_component_Comp, [() => ({
[_ctx.arg]: _ctx.foo,
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)
}], true)
})], true)
return n0
}"
`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,54 @@ describe('compiler: element transform', () => {
expect(code).contains('_setDynamicEvents(n0, _ctx.obj)')
})

test('component with dynamic prop arguments', () => {
const { code, ir } = compileWithElementTransform(
`<Foo :[foo-bar]="bar" :[baz]="qux" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [
{
key: { content: 'foo-bar' },
values: [{ content: 'bar' }],
},
{
key: { content: 'baz' },
values: [{ content: 'qux' }],
},
],
},
])
})

test('component with dynamic event arguments', () => {
const { code, ir } = compileWithElementTransform(
`<Foo @[foo-bar]="bar" @[baz]="qux" />`,
)
expect(code).toMatchSnapshot()
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [
{
key: { content: 'foo-bar' },
values: [{ content: 'bar' }],
handler: true,
},
{
key: { content: 'baz' },
values: [{ content: 'qux' }],
handler: true,
},
],
},
])
})

test('invalid html nesting', () => {
const { code, ir } = compileWithElementTransform(
`<p><div>123</div></p>
Expand Down
42 changes: 19 additions & 23 deletions packages/compiler-vapor/__tests__/transforms/vModel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,22 +259,20 @@ describe('compiler: vModel transform', () => {
const { code, ir } = compileWithVModel('<Comp v-model:[arg]="foo" />')
expect(code).toMatchSnapshot()
expect(code).contains(
`[_ctx.arg]: () => (_ctx.foo),
`[_ctx.arg]: _ctx.foo,
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)`,
)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
props: [
[
{
key: { content: 'arg', isStatic: false },
values: [{ content: 'foo', isStatic: false }],
model: true,
modelModifiers: [],
},
],
{
key: { content: 'arg', isStatic: false },
values: [{ content: 'foo', isStatic: false }],
model: true,
modelModifiers: [],
},
],
},
])
Expand Down Expand Up @@ -349,20 +347,18 @@ describe('compiler: vModel transform', () => {
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Comp',
props: [
[
{
key: { content: 'foo', isStatic: false },
values: [{ content: 'foo', isStatic: false }],
model: true,
modelModifiers: ['trim'],
},
{
key: { content: 'bar', isStatic: false },
values: [{ content: 'bar', isStatic: false }],
model: true,
modelModifiers: ['number'],
},
],
{
key: { content: 'foo', isStatic: false },
values: [{ content: 'foo', isStatic: false }],
model: true,
modelModifiers: ['trim'],
},
{
key: { content: 'bar', isStatic: false },
values: [{ content: 'bar', isStatic: false }],
model: true,
modelModifiers: ['number'],
},
],
},
])
Expand Down
126 changes: 73 additions & 53 deletions packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { camelize, extend, isArray } from '@vue/shared'
import type { CodegenContext } from '../generate'
import type { CreateComponentIRNode, IRProp } from '../ir'
import type { CreateComponentIRNode, IRProp, IRProps } from '../ir'
import {
type CodeFragment,
NEWLINE,
Expand All @@ -21,11 +21,11 @@ export function genCreateComponent(
oper: CreateComponentIRNode,
context: CodegenContext,
): CodeFragment[] {
const { helper, vaporHelper } = context
const { vaporHelper } = context

const tag = genTag()
const isRoot = oper.root
const rawProps = genRawProps()
const rawProps = genRawProps(oper.props, context)

return [
NEWLINE,
Expand All @@ -49,63 +49,83 @@ export function genCreateComponent(
)
}
}
}

function genRawProps() {
const props = oper.props
.map(props => {
if (isArray(props)) {
if (!props.length) return
return genStaticProps(props)
} else {
let expr = genExpression(props.value, context)
if (props.handler) expr = genCall(helper('toHandlers'), expr)
return ['() => (', ...expr, ')']
export function genRawProps(props: IRProps[], context: CodegenContext) {
const frag = props
.map(props => {
if (isArray(props)) {
if (!props.length) return
return genStaticProps(props, context)
} else {
let expr: CodeFragment[]
if ('key' in props)
expr = genMulti(
SEGMENTS_OBJECT_NEWLINE,
genProp(props, context, false),
)
else {
expr = genExpression(props.value, context)
if (props.handler) expr = genCall(context.helper('toHandlers'), expr)
}
})
.filter(Boolean)
if (props.length) {
return genMulti(SEGMENTS_ARRAY, ...props)
}
return ['() => (', ...expr, ')']
}
})
.filter(
Boolean as any as (v: CodeFragment[] | undefined) => v is CodeFragment[],
)
if (frag.length) {
return genMulti(SEGMENTS_ARRAY, ...frag)
}
}

function genStaticProps(props: IRProp[]) {
return genMulti(
SEGMENTS_OBJECT_NEWLINE,
...props.map(prop => {
return [
...genPropKey(prop, context),
': ',
...(prop.handler
? genEventHandler(context, prop.values[0])
: ['() => (', ...genExpression(prop.values[0], context), ')']),
...(prop.model
? [...genModelEvent(prop), ...genModelModifiers(prop)]
: []),
]
}),
)
function genStaticProps(
props: IRProp[],
context: CodegenContext,
): CodeFragment[] {
return genMulti(
SEGMENTS_OBJECT_NEWLINE,
...props.map(prop => genProp(prop, context, true)),
)
}

function genProp(prop: IRProp, context: CodegenContext, isStaticArg: boolean) {
return [
...genPropKey(prop, context),
': ',
...(prop.handler
? genEventHandler(context, prop.values[0])
: isStaticArg
? ['() => (', ...genExpression(prop.values[0], context), ')']
: genExpression(prop.values[0], context)),
...(prop.model
? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)]
: []),
]
}

function genModelEvent(prop: IRProp): CodeFragment[] {
const name = prop.key.isStatic
? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)]
: ['["onUpdate:" + ', ...genExpression(prop.key, context), ']']
const handler = genModelHandler(prop.values[0], context)
function genModelEvent(prop: IRProp, context: CodegenContext): CodeFragment[] {
const name = prop.key.isStatic
? [JSON.stringify(`onUpdate:${camelize(prop.key.content)}`)]
: ['["onUpdate:" + ', ...genExpression(prop.key, context), ']']
const handler = genModelHandler(prop.values[0], context)

return [',', NEWLINE, ...name, ': ', ...handler]
}
return [',', NEWLINE, ...name, ': ', ...handler]
}

function genModelModifiers(prop: IRProp): CodeFragment[] {
const { key, modelModifiers } = prop
if (!modelModifiers || !modelModifiers.length) return []
function genModelModifiers(
prop: IRProp,
context: CodegenContext,
): CodeFragment[] {
const { key, modelModifiers } = prop
if (!modelModifiers || !modelModifiers.length) return []

const modifiersKey = key.isStatic
? key.content === 'modelValue'
? [`modelModifiers`]
: [`${key.content}Modifiers`]
: ['[', ...genExpression(key, context), ' + "Modifiers"]']
const modifiersKey = key.isStatic
? key.content === 'modelValue'
? [`modelModifiers`]
: [`${key.content}Modifiers`]
: ['[', ...genExpression(key, context), ' + "Modifiers"]']

const modifiersVal = genDirectiveModifiers(modelModifiers)
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
}
}
const modifiersVal = genDirectiveModifiers(modelModifiers)
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
}
4 changes: 3 additions & 1 deletion packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ export function genDynamicProps(
props =>
Array.isArray(props)
? genLiteralObjectProps(props, context) // static and dynamic arg props
: genExpression(props.value, context), // v-bind=""
: 'key' in props
? genLiteralObjectProps([props], context) // dynamic arg props
: genExpression(props.value, context), // v-bind=""
),
),
]
Expand Down
11 changes: 5 additions & 6 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,11 @@ export interface ForIRNode extends BaseIRNode {
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
values: SimpleExpressionNode[]
}
export type IRProps =
| IRProp[]
| {
value: SimpleExpressionNode
handler?: boolean
}
export interface IRDynamicProps {
value: SimpleExpressionNode
handler?: boolean
}
export type IRProps = IRProp[] | IRProp | IRDynamicProps

export interface SetPropIRNode extends BaseIRNode {
type: IRNodeTypes.SET_PROP
Expand Down
9 changes: 8 additions & 1 deletion packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,15 @@ function buildProps(

const result = transformProp(prop, node, context)
if (result) {
results.push(result)
dynamicExpr.push(result.key, result.value)
if (isComponent && !result.key.isStatic) {
// v-bind:[name]="value" or v-on:[name]="value"
pushMergeArg()
dynamicArgs.push(normalizeIRProp(result))
} else {
// other static props
results.push(result)
}
}
}

Expand Down

0 comments on commit e7531d1

Please sign in to comment.