Skip to content

feat(compiler-vapor): component with dynamic arguments #192

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 4 commits into from
Apr 30, 2024
Merged
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
@@ -100,9 +100,9 @@ exports[`compiler: element transform > component > should wrap as function if v-

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [{
onBar: () => $event => (_ctx.handleBar($event))
}], true)
const n0 = _createComponent(_component_Foo, [
{ onBar: () => $event => (_ctx.handleBar($event)) }
], true)
return n0
}"
`;
@@ -112,10 +112,12 @@ exports[`compiler: element transform > component > static props 1`] = `

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [{
id: () => ("foo"),
class: () => ("bar")
}], true)
const n0 = _createComponent(_component_Foo, [
{
id: () => ("foo"),
class: () => ("bar")
}
], true)
return n0
}"
`;
@@ -125,7 +127,9 @@ exports[`compiler: element transform > component > v-bind="obj" 1`] = `

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [() => (_ctx.obj)], true)
const n0 = _createComponent(_component_Foo, [
() => (_ctx.obj)
], true)
return n0
}"
`;
@@ -135,9 +139,10 @@ exports[`compiler: element transform > component > v-bind="obj" after static pro

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [{
id: () => ("foo")
}, () => (_ctx.obj)], true)
const n0 = _createComponent(_component_Foo, [
{ id: () => ("foo") },
() => (_ctx.obj)
], true)
return n0
}"
`;
@@ -147,9 +152,10 @@ exports[`compiler: element transform > component > v-bind="obj" before static pr

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [() => (_ctx.obj), {
id: () => ("foo")
}], true)
const n0 = _createComponent(_component_Foo, [
() => (_ctx.obj),
{ id: () => ("foo") }
], true)
return n0
}"
`;
@@ -159,11 +165,11 @@ exports[`compiler: element transform > component > v-bind="obj" between static p

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [{
id: () => ("foo")
}, () => (_ctx.obj), {
class: () => ("bar")
}], true)
const n0 = _createComponent(_component_Foo, [
{ id: () => ("foo") },
() => (_ctx.obj),
{ class: () => ("bar") }
], true)
return n0
}"
`;
@@ -174,7 +180,36 @@ import { resolveComponent as _resolveComponent, createComponent as _createCompon

export function render(_ctx) {
const _component_Foo = _resolveComponent("Foo")
const n0 = _createComponent(_component_Foo, [() => (_toHandlers(_ctx.obj))], true)
const n0 = _createComponent(_component_Foo, [
() => (_toHandlers(_ctx.obj))
], true)
return n0
}"
`;

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
}"
`;
Original file line number Diff line number Diff line change
@@ -5,11 +5,11 @@ exports[`compiler: vModel transform > component > v-model for component should g

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
modelValue: () => (_ctx.foo),
const n0 = _createComponent(_component_Comp, [
{ modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event),
modelModifiers: () => ({ trim: true, "bar-baz": true })
}], true)
modelModifiers: () => ({ trim: true, "bar-baz": true }) }
], true)
return n0
}"
`;
@@ -19,10 +19,10 @@ exports[`compiler: vModel transform > component > v-model for component should w

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event)
}], true)
const n0 = _createComponent(_component_Comp, [
{ modelValue: () => (_ctx.foo),
"onUpdate:modelValue": () => $event => (_ctx.foo = $event) }
], true)
return n0
}"
`;
@@ -32,14 +32,16 @@ exports[`compiler: vModel transform > component > v-model with arguments for com

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
foo: () => (_ctx.foo),
"onUpdate:foo": () => $event => (_ctx.foo = $event),
fooModifiers: () => ({ trim: true }),
bar: () => (_ctx.bar),
"onUpdate:bar": () => $event => (_ctx.bar = $event),
barModifiers: () => ({ number: true })
}], true)
const n0 = _createComponent(_component_Comp, [
{
foo: () => (_ctx.foo),
"onUpdate:foo": () => $event => (_ctx.foo = $event),
fooModifiers: () => ({ trim: true }),
bar: () => (_ctx.bar),
"onUpdate:bar": () => $event => (_ctx.bar = $event),
barModifiers: () => ({ number: true })
}
], true)
return n0
}"
`;
@@ -49,10 +51,10 @@ exports[`compiler: vModel transform > component > v-model with arguments for com

export function render(_ctx) {
const _component_Comp = _resolveComponent("Comp")
const n0 = _createComponent(_component_Comp, [{
bar: () => (_ctx.foo),
"onUpdate:bar": () => $event => (_ctx.foo = $event)
}], true)
const n0 = _createComponent(_component_Comp, [
{ bar: () => (_ctx.foo),
"onUpdate:bar": () => $event => (_ctx.foo = $event) }
], true)
return n0
}"
`;
@@ -62,14 +64,14 @@ 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)
[_ctx.bar + "Modifiers"]: () => ({ number: true }) })
], true)
return n0
}"
`;
@@ -79,10 +81,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),
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event)
}], true)
const n0 = _createComponent(_component_Comp, [
() => ({ [_ctx.arg]: _ctx.foo,
["onUpdate:" + _ctx.arg]: () => $event => (_ctx.foo = $event) })
], true)
return n0
}"
`;
133 changes: 113 additions & 20 deletions packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { makeCompile } from './_utils'
import {
IRDynamicPropsKind,
IRNodeTypes,
transformChildren,
transformElement,
@@ -198,10 +199,12 @@ describe('compiler: element transform', () => {
)

expect(code).toMatchSnapshot()
expect(code).contains('_createComponent(_component_Foo, [{')
expect(code).contains(' id: () => ("foo")')
expect(code).contains(' class: () => ("bar")')
expect(code).contains('}], true)')
expect(code).contains(`[
{
id: () => ("foo"),
class: () => ("bar")
}
]`)

expect(ir.block.operation).toMatchObject([
{
@@ -248,12 +251,19 @@ describe('compiler: element transform', () => {
test('v-bind="obj"', () => {
const { code, ir } = compileWithElementTransform(`<Foo v-bind="obj" />`)
expect(code).toMatchSnapshot()
expect(code).contains('[() => (_ctx.obj)]')
expect(code).contains(`[
() => (_ctx.obj)
]`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [{ value: { content: 'obj', isStatic: false } }],
props: [
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj', isStatic: false },
},
],
},
])
})
@@ -263,15 +273,20 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" />`,
)
expect(code).toMatchSnapshot()
expect(code).contains('id: () => ("foo")')
expect(code).contains('}, () => (_ctx.obj)]')
expect(code).contains(`[
{ id: () => ("foo") },
() => (_ctx.obj)
]`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
{ value: { content: 'obj' } },
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
},
],
},
])
@@ -282,14 +297,19 @@ describe('compiler: element transform', () => {
`<Foo v-bind="obj" id="foo" />`,
)
expect(code).toMatchSnapshot()
expect(code).contains('[() => (_ctx.obj), {')
expect(code).contains('id: () => ("foo")')
expect(code).contains(`[
() => (_ctx.obj),
{ id: () => ("foo") }
]`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [
{ value: { content: 'obj' } },
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
},
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
],
},
@@ -301,16 +321,21 @@ describe('compiler: element transform', () => {
`<Foo id="foo" v-bind="obj" class="bar" />`,
)
expect(code).toMatchSnapshot()
expect(code).contains('id: () => ("foo")')
expect(code).contains('}, () => (_ctx.obj), {')
expect(code).contains('class: () => ("bar")')
expect(code).contains(`[
{ id: () => ("foo") },
() => (_ctx.obj),
{ class: () => ("bar") }
]`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
{ value: { content: 'obj' } },
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
},
[{ key: { content: 'class' }, values: [{ content: 'bar' }] }],
],
},
@@ -356,12 +381,20 @@ describe('compiler: element transform', () => {
test('v-on="obj"', () => {
const { code, ir } = compileWithElementTransform(`<Foo v-on="obj" />`)
expect(code).toMatchSnapshot()
expect(code).contains('[() => (_toHandlers(_ctx.obj))]')
expect(code).contains(`[
() => (_toHandlers(_ctx.obj))
]`)
expect(ir.block.operation).toMatchObject([
{
type: IRNodeTypes.CREATE_COMPONENT_NODE,
tag: 'Foo',
props: [{ value: { content: 'obj' }, handler: true }],
props: [
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
handler: true,
},
],
},
])
})
@@ -432,6 +465,7 @@ describe('compiler: element transform', () => {
element: 0,
props: [
{
kind: IRDynamicPropsKind.EXPRESSION,
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'obj',
@@ -467,6 +501,7 @@ describe('compiler: element transform', () => {
props: [
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
{
kind: IRDynamicPropsKind.EXPRESSION,
value: {
type: NodeTypes.SIMPLE_EXPRESSION,
content: 'obj',
@@ -494,7 +529,10 @@ describe('compiler: element transform', () => {
type: IRNodeTypes.SET_DYNAMIC_PROPS,
element: 0,
props: [
{ value: { content: 'obj' } },
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
},
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
],
},
@@ -518,7 +556,10 @@ describe('compiler: element transform', () => {
element: 0,
props: [
[{ key: { content: 'id' }, values: [{ content: 'foo' }] }],
{ value: { content: 'obj' } },
{
kind: IRDynamicPropsKind.EXPRESSION,
value: { content: 'obj' },
},
[{ key: { content: 'class' }, values: [{ content: 'bar' }] }],
],
},
@@ -691,6 +732,58 @@ 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: [
{
kind: IRDynamicPropsKind.ATTRIBUTE,
key: { content: 'foo-bar' },
values: [{ content: 'bar' }],
},
{
kind: IRDynamicPropsKind.ATTRIBUTE,
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: [
{
kind: IRDynamicPropsKind.ATTRIBUTE,
key: { content: 'foo-bar' },
values: [{ content: 'bar' }],
handler: true,
},
{
kind: IRDynamicPropsKind.ATTRIBUTE,
key: { content: 'baz' },
values: [{ content: 'qux' }],
handler: true,
},
],
},
])
})

test('invalid html nesting', () => {
const { code, ir } = compileWithElementTransform(
`<p><div>123</div></p>
42 changes: 19 additions & 23 deletions packages/compiler-vapor/__tests__/transforms/vModel.spec.ts
Original file line number Diff line number Diff line change
@@ -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: [],
},
],
},
])
@@ -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'],
},
],
},
])
132 changes: 78 additions & 54 deletions packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { camelize, extend, isArray } from '@vue/shared'
import type { CodegenContext } from '../generate'
import type { CreateComponentIRNode, IRProp } from '../ir'
import {
type CreateComponentIRNode,
IRDynamicPropsKind,
type IRProp,
type IRProps,
type IRPropsStatic,
} from '../ir'
import {
type CodeFragment,
NEWLINE,
SEGMENTS_ARRAY,
SEGMENTS_ARRAY_NEWLINE,
SEGMENTS_OBJECT,
SEGMENTS_OBJECT_NEWLINE,
genCall,
genMulti,
@@ -21,11 +28,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,
@@ -49,63 +56,80 @@ 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 (props.kind === IRDynamicPropsKind.ATTRIBUTE)
expr = genMulti(SEGMENTS_OBJECT, genProp(props, context))
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_NEWLINE, ...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: IRPropsStatic,
context: CodegenContext,
): CodeFragment[] {
return genMulti(
props.length > 1 ? SEGMENTS_OBJECT_NEWLINE : SEGMENTS_OBJECT,
...props.map(prop => genProp(prop, context, true)),
)
}

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 genProp(prop: IRProp, context: CodegenContext, isStatic?: boolean) {
return [
...genPropKey(prop, context),
': ',
...(prop.handler
? genEventHandler(context, prop.values[0])
: isStatic
? ['() => (', ...genExpression(prop.values[0], context), ')']
: genExpression(prop.values[0], context)),
...(prop.model
? [...genModelEvent(prop, context), ...genModelModifiers(prop, context)]
: []),
]
}

return [',', NEWLINE, ...name, ': ', ...handler]
}
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)

function genModelModifiers(prop: IRProp): CodeFragment[] {
const { key, modelModifiers } = prop
if (!modelModifiers || !modelModifiers.length) return []
return [',', NEWLINE, ...name, ': ', ...handler]
}

const modifiersKey = key.isStatic
? key.content === 'modelValue'
? [`modelModifiers`]
: [`${key.content}Modifiers`]
: ['[', ...genExpression(key, context), ' + "Modifiers"]']
function genModelModifiers(
prop: IRProp,
context: CodegenContext,
): CodeFragment[] {
const { key, modelModifiers } = prop
if (!modelModifiers || !modelModifiers.length) return []

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

const modifiersVal = genDirectiveModifiers(modelModifiers)
return [',', NEWLINE, ...modifiersKey, `: () => ({ ${modifiersVal} })`]
}
15 changes: 9 additions & 6 deletions packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
@@ -4,11 +4,12 @@ import {
isSimpleIdentifier,
} from '@vue/compiler-core'
import type { CodegenContext } from '../generate'
import type {
IRProp,
SetDynamicPropsIRNode,
SetPropIRNode,
VaporHelper,
import {
IRDynamicPropsKind,
type IRProp,
type SetDynamicPropsIRNode,
type SetPropIRNode,
type VaporHelper,
} from '../ir'
import { genExpression } from './expression'
import {
@@ -73,7 +74,9 @@ export function genDynamicProps(
props =>
Array.isArray(props)
? genLiteralObjectProps(props, context) // static and dynamic arg props
: genExpression(props.value, context), // v-bind=""
: props.kind === IRDynamicPropsKind.ATTRIBUTE
? genLiteralObjectProps([props], context) // dynamic arg props
: genExpression(props.value, context), // v-bind=""
),
),
]
5 changes: 5 additions & 0 deletions packages/compiler-vapor/src/generators/utils.ts
Original file line number Diff line number Diff line change
@@ -57,6 +57,11 @@ export function genMulti(
}
}
export const SEGMENTS_ARRAY: Segments = ['[', ']', ', ']
export const SEGMENTS_ARRAY_NEWLINE: Segments = [
['[', INDENT_START, NEWLINE],
[INDENT_END, NEWLINE, ']'],
[', ', NEWLINE],
]
export const SEGMENTS_OBJECT: Segments = ['{ ', ' }', ', ']
export const SEGMENTS_OBJECT_NEWLINE: Segments = [
['{', INDENT_START, NEWLINE],
23 changes: 18 additions & 5 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
@@ -83,12 +83,25 @@ export interface ForIRNode extends BaseIRNode {
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
values: SimpleExpressionNode[]
}

export enum IRDynamicPropsKind {
EXPRESSION, // v-bind="value"
ATTRIBUTE, // v-bind:[foo]="value"
}

export type IRPropsStatic = IRProp[]
export interface IRPropsDynamicExpression {
kind: IRDynamicPropsKind.EXPRESSION
value: SimpleExpressionNode
handler?: boolean
}
export interface IRPropsDynamicAttribute extends IRProp {
kind: IRDynamicPropsKind.ATTRIBUTE
}
export type IRProps =
| IRProp[]
| {
value: SimpleExpressionNode
handler?: boolean
}
| IRPropsStatic
| IRPropsDynamicAttribute
| IRPropsDynamicExpression

export interface SetPropIRNode extends BaseIRNode {
type: IRNodeTypes.SET_PROP
39 changes: 31 additions & 8 deletions packages/compiler-vapor/src/transforms/transformElement.ts
Original file line number Diff line number Diff line change
@@ -24,9 +24,11 @@ import type {
} from '../transform'
import {
DynamicFlag,
IRDynamicPropsKind,
IRNodeTypes,
type IRProp,
type IRProps,
type IRPropsDynamicAttribute,
type VaporDirectiveNode,
} from '../ir'
import { EMPTY_EXPRESSION } from './utils'
@@ -205,7 +207,10 @@ function buildProps(
if (prop.exp) {
dynamicExpr.push(prop.exp)
pushMergeArg()
dynamicArgs.push({ value: prop.exp })
dynamicArgs.push({
kind: IRDynamicPropsKind.EXPRESSION,
value: prop.exp,
})
} else {
context.options.onError(
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc),
@@ -218,7 +223,11 @@ function buildProps(
if (isComponent) {
dynamicExpr.push(prop.exp)
pushMergeArg()
dynamicArgs.push({ value: prop.exp, handler: true })
dynamicArgs.push({
kind: IRDynamicPropsKind.EXPRESSION,
value: prop.exp,
handler: true,
})
} else {
context.registerEffect(
[prop.exp],
@@ -241,8 +250,19 @@ 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(
extend(resolveDirectiveResult(result), {
kind: IRDynamicPropsKind.ATTRIBUTE,
}) as IRPropsDynamicAttribute,
)
} else {
// other static props
results.push(result)
}
}
}

@@ -297,7 +317,7 @@ function dedupeProperties(results: DirectiveTransformResult[]): IRProp[] {
const deduped: IRProp[] = []

for (const result of results) {
const prop = normalizeIRProp(result)
const prop = resolveDirectiveResult(result)
// dynamic keys are always allowed
if (!prop.key.isStatic) {
deduped.push(prop)
@@ -307,7 +327,7 @@ function dedupeProperties(results: DirectiveTransformResult[]): IRProp[] {
const existing = knownProps.get(name)
if (existing) {
if (name === 'style' || name === 'class') {
mergeAsArray(existing, prop)
mergePropValues(existing, prop)
}
// unexpected duplicate, should have emitted error during parse
} else {
@@ -318,11 +338,14 @@ function dedupeProperties(results: DirectiveTransformResult[]): IRProp[] {
return deduped
}

function normalizeIRProp(prop: DirectiveTransformResult): IRProp {
return extend({}, prop, { value: undefined, values: [prop.value] })
function resolveDirectiveResult(prop: DirectiveTransformResult): IRProp {
return extend({}, prop, {
value: undefined,
values: [prop.value],
})
}

function mergeAsArray(existing: IRProp, incoming: IRProp) {
function mergePropValues(existing: IRProp, incoming: IRProp) {
const newValues = incoming.values
existing.values.push(...newValues)
}