feat(compiler-vapor): props merging (#118)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
75b0937d31
commit
ba3ca6a304
|
@ -0,0 +1,29 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler: element transform > props merging: class 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setClass as _setClass } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setClass(n1, ["foo", { bar: _ctx.isBar }])
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: element transform > props merging: style 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setStyle as _setStyle } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setStyle(n1, ["color: green", { color: 'red' }])
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
|
@ -1,2 +1,145 @@
|
|||
// TODO: add tests for this transform
|
||||
test('baisc', () => {})
|
||||
import { makeCompile } from './_utils'
|
||||
import {
|
||||
IRNodeTypes,
|
||||
transformElement,
|
||||
transformVBind,
|
||||
transformVOn,
|
||||
} from '../../src'
|
||||
import { NodeTypes } from '@vue/compiler-core'
|
||||
|
||||
const compileWithElementTransform = makeCompile({
|
||||
nodeTransforms: [transformElement],
|
||||
directiveTransforms: {
|
||||
bind: transformVBind,
|
||||
on: transformVOn,
|
||||
},
|
||||
})
|
||||
|
||||
describe('compiler: element transform', () => {
|
||||
test.todo('baisc')
|
||||
|
||||
test.todo('props merging: event handlers', () => {
|
||||
const { code, ir } = compileWithElementTransform(
|
||||
`<div @click.foo="a" @click.bar="b" />`,
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
|
||||
expect(ir.operation).toMatchObject([
|
||||
{
|
||||
type: IRNodeTypes.SET_EVENT,
|
||||
element: 1,
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'click',
|
||||
isStatic: true,
|
||||
},
|
||||
events: [
|
||||
{
|
||||
// IREvent: value, modifiers, keyOverride...
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `a`,
|
||||
isStatic: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `b`,
|
||||
isStatic: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test('props merging: style', () => {
|
||||
const { code, ir } = compileWithElementTransform(
|
||||
`<div style="color: green" :style="{ color: 'red' }" />`,
|
||||
)
|
||||
expect(code).toMatchSnapshot()
|
||||
|
||||
expect(ir.effect).toMatchObject([
|
||||
{
|
||||
expressions: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ color: 'red' }`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
operations: [
|
||||
{
|
||||
type: IRNodeTypes.SET_PROP,
|
||||
element: 1,
|
||||
prop: {
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'style',
|
||||
isStatic: true,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'color: green',
|
||||
isStatic: true,
|
||||
},
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ color: 'red' }`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test('props merging: class', () => {
|
||||
const { code, ir } = compileWithElementTransform(
|
||||
`<div class="foo" :class="{ bar: isBar }" />`,
|
||||
)
|
||||
|
||||
expect(code).toMatchSnapshot()
|
||||
|
||||
expect(ir.effect).toMatchObject([
|
||||
{
|
||||
expressions: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ bar: isBar }`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
operations: [
|
||||
{
|
||||
type: IRNodeTypes.SET_PROP,
|
||||
element: 1,
|
||||
prop: {
|
||||
key: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'class',
|
||||
isStatic: true,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `foo`,
|
||||
isStatic: true,
|
||||
},
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `{ bar: isBar }`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
|
|
|
@ -52,16 +52,18 @@ describe('compiler v-bind', () => {
|
|||
source: 'id',
|
||||
},
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
loc: {
|
||||
source: 'id',
|
||||
start: { line: 1, column: 17, offset: 16 },
|
||||
end: { line: 1, column: 19, offset: 18 },
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
loc: {
|
||||
source: 'id',
|
||||
start: { line: 1, column: 17, offset: 16 },
|
||||
end: { line: 1, column: 19, offset: 18 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
loc: {
|
||||
start: { column: 6, line: 1, offset: 5 },
|
||||
end: { column: 20, line: 1, offset: 19 },
|
||||
|
@ -92,14 +94,16 @@ describe('compiler v-bind', () => {
|
|||
end: { line: 1, column: 15, offset: 14 },
|
||||
},
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
loc: {
|
||||
start: { line: 1, column: 13, offset: 12 },
|
||||
end: { line: 1, column: 15, offset: 14 },
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
loc: {
|
||||
start: { line: 1, column: 13, offset: 12 },
|
||||
end: { line: 1, column: 15, offset: 14 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
expect(code).contains('_setDynamicProp(n1, "id", _ctx.id)')
|
||||
|
@ -116,10 +120,12 @@ describe('compiler v-bind', () => {
|
|||
content: `camel-case`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `camelCase`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `camelCase`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
expect(code).contains('_setDynamicProp(n1, "camel-case", _ctx.camelCase)')
|
||||
|
@ -141,11 +147,13 @@ describe('compiler v-bind', () => {
|
|||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: {
|
||||
|
@ -153,11 +161,13 @@ describe('compiler v-bind', () => {
|
|||
content: 'title',
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'title',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'title',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
|
@ -183,11 +193,13 @@ describe('compiler v-bind', () => {
|
|||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'id',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: {
|
||||
|
@ -195,11 +207,13 @@ describe('compiler v-bind', () => {
|
|||
content: 'foo',
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'bar',
|
||||
isStatic: true,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'bar',
|
||||
isStatic: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: {
|
||||
|
@ -247,10 +261,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: undefined,
|
||||
},
|
||||
|
@ -270,10 +286,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: undefined,
|
||||
},
|
||||
|
@ -294,10 +312,12 @@ describe('compiler v-bind', () => {
|
|||
content: `foo`,
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: true,
|
||||
modifier: undefined,
|
||||
},
|
||||
|
@ -324,10 +344,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
},
|
||||
|
@ -346,10 +368,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
},
|
||||
|
@ -371,10 +395,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
},
|
||||
|
@ -399,10 +425,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
},
|
||||
|
@ -421,10 +449,12 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
},
|
||||
|
@ -443,10 +473,12 @@ describe('compiler v-bind', () => {
|
|||
content: `foo-bar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '^',
|
||||
},
|
||||
|
@ -465,10 +497,12 @@ describe('compiler v-bind', () => {
|
|||
content: `foo-bar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
runtimeCamelize: false,
|
||||
modifier: '^',
|
||||
},
|
||||
|
|
|
@ -50,11 +50,13 @@ describe('compiler: v-once', () => {
|
|||
content: 'class',
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'clz',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'clz',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -81,11 +83,13 @@ describe('compiler: v-once', () => {
|
|||
content: 'id',
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'foo',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'foo',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
@ -111,11 +115,13 @@ describe('compiler: v-once', () => {
|
|||
content: 'id',
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'foo',
|
||||
isStatic: false,
|
||||
},
|
||||
values: [
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'foo',
|
||||
isStatic: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
])
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
import {
|
||||
NewlineType,
|
||||
type SimpleExpressionNode,
|
||||
isSimpleIdentifier,
|
||||
} from '@vue/compiler-core'
|
||||
import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
|
||||
import type { SetDynamicPropsIRNode, SetPropIRNode, VaporHelper } from '../ir'
|
||||
import type {
|
||||
IRProp,
|
||||
SetDynamicPropsIRNode,
|
||||
SetPropIRNode,
|
||||
VaporHelper,
|
||||
} from '../ir'
|
||||
import { genExpression } from './expression'
|
||||
import type { DirectiveTransformResult } from '../transform'
|
||||
import { NewlineType, isSimpleIdentifier } from '@vue/compiler-core'
|
||||
|
||||
// only the static key prop will reach here
|
||||
export function genSetProp(
|
||||
|
@ -11,7 +19,7 @@ export function genSetProp(
|
|||
): CodeFragment[] {
|
||||
const { call, vaporHelper } = context
|
||||
const {
|
||||
prop: { key, value, modifier },
|
||||
prop: { key, values, modifier },
|
||||
} = oper
|
||||
|
||||
const keyName = key.content
|
||||
|
@ -36,7 +44,7 @@ export function genSetProp(
|
|||
vaporHelper(helperName),
|
||||
`n${oper.element}`,
|
||||
omitKey ? false : genExpression(key, context),
|
||||
genExpression(value, context),
|
||||
genPropValue(values, context),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
@ -63,7 +71,7 @@ export function genDynamicProps(
|
|||
}
|
||||
|
||||
function genLiteralObjectProps(
|
||||
props: DirectiveTransformResult[],
|
||||
props: IRProp[],
|
||||
context: CodegenContext,
|
||||
): CodeFragment[] {
|
||||
const { multi } = context
|
||||
|
@ -72,13 +80,13 @@ function genLiteralObjectProps(
|
|||
...props.map(prop => [
|
||||
...genPropertyKey(prop, context),
|
||||
`: `,
|
||||
...genExpression(prop.value, context),
|
||||
...genPropValue(prop.values, context),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
||||
function genPropertyKey(
|
||||
{ key: node, runtimeCamelize, modifier }: DirectiveTransformResult,
|
||||
{ key: node, runtimeCamelize, modifier }: IRProp,
|
||||
context: CodegenContext,
|
||||
): CodeFragment[] {
|
||||
const { call, helper } = context
|
||||
|
@ -111,3 +119,14 @@ function genPropertyKey(
|
|||
|
||||
return [`[`, ...key, `]`]
|
||||
}
|
||||
|
||||
function genPropValue(values: SimpleExpressionNode[], context: CodegenContext) {
|
||||
if (values.length === 1) {
|
||||
return genExpression(values[0], context)
|
||||
}
|
||||
const { multi } = context
|
||||
return multi(
|
||||
['[', ']', ', '],
|
||||
...values.map(expr => genExpression(expr, context)),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -88,18 +88,21 @@ export interface FragmentFactoryIRNode extends BaseIRNode {
|
|||
type: IRNodeTypes.FRAGMENT_FACTORY
|
||||
}
|
||||
|
||||
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
|
||||
values: SimpleExpressionNode[]
|
||||
}
|
||||
export type IRProps = IRProp[] | SimpleExpressionNode
|
||||
|
||||
export interface SetPropIRNode extends BaseIRNode {
|
||||
type: IRNodeTypes.SET_PROP
|
||||
element: number
|
||||
prop: DirectiveTransformResult
|
||||
prop: IRProp
|
||||
}
|
||||
|
||||
export type PropsExpression = DirectiveTransformResult[] | SimpleExpressionNode
|
||||
|
||||
export interface SetDynamicPropsIRNode extends BaseIRNode {
|
||||
type: IRNodeTypes.SET_DYNAMIC_PROPS
|
||||
element: number
|
||||
props: PropsExpression[]
|
||||
props: IRProps[]
|
||||
}
|
||||
|
||||
export interface SetTextIRNode extends BaseIRNode {
|
||||
|
|
|
@ -8,7 +8,12 @@ import {
|
|||
createCompilerError,
|
||||
createSimpleExpression,
|
||||
} from '@vue/compiler-dom'
|
||||
import { isBuiltInDirective, isReservedProp, isVoidTag } from '@vue/shared'
|
||||
import {
|
||||
extend,
|
||||
isBuiltInDirective,
|
||||
isReservedProp,
|
||||
isVoidTag,
|
||||
} from '@vue/shared'
|
||||
import type {
|
||||
DirectiveTransformResult,
|
||||
NodeTransform,
|
||||
|
@ -16,7 +21,8 @@ import type {
|
|||
} from '../transform'
|
||||
import {
|
||||
IRNodeTypes,
|
||||
type PropsExpression,
|
||||
type IRProp,
|
||||
type IRProps,
|
||||
type VaporDirectiveNode,
|
||||
} from '../ir'
|
||||
|
||||
|
@ -61,7 +67,7 @@ function buildProps(
|
|||
props: (VaporDirectiveNode | AttributeNode)[] = node.props as any,
|
||||
isComponent: boolean,
|
||||
) {
|
||||
const dynamicArgs: PropsExpression[] = []
|
||||
const dynamicArgs: IRProps[] = []
|
||||
const dynamicExpr: SimpleExpressionNode[] = []
|
||||
let results: DirectiveTransformResult[] = []
|
||||
|
||||
|
@ -75,19 +81,11 @@ function buildProps(
|
|||
|
||||
function pushMergeArg() {
|
||||
if (results.length) {
|
||||
dynamicArgs.push(results)
|
||||
dynamicArgs.push(dedupeProperties(results))
|
||||
results = []
|
||||
}
|
||||
}
|
||||
|
||||
// treat all props as dynamic key
|
||||
const asDynamic = props.some(
|
||||
prop =>
|
||||
prop.type === NodeTypes.DIRECTIVE &&
|
||||
prop.name === 'bind' &&
|
||||
(!prop.arg || !prop.arg.isStatic),
|
||||
)
|
||||
|
||||
for (const prop of props) {
|
||||
if (
|
||||
prop.type === NodeTypes.DIRECTIVE &&
|
||||
|
@ -106,20 +104,17 @@ function buildProps(
|
|||
continue
|
||||
}
|
||||
|
||||
const result = transformProp(prop, node, context, asDynamic)
|
||||
const result = transformProp(prop, node, context)
|
||||
if (result) {
|
||||
results.push(result)
|
||||
asDynamic && pushDynamicExpressions(result.key, result.value)
|
||||
pushDynamicExpressions(result.key, result.value)
|
||||
}
|
||||
}
|
||||
|
||||
// take rest of props as dynamic props
|
||||
if (dynamicArgs.length || results.some(({ key }) => !key.isStatic)) {
|
||||
pushMergeArg()
|
||||
}
|
||||
|
||||
// has dynamic key or v-bind="{}"
|
||||
if (dynamicArgs.length) {
|
||||
if (dynamicArgs.length || results.some(({ key }) => !key.isStatic)) {
|
||||
// take rest of props as dynamic props
|
||||
pushMergeArg()
|
||||
context.registerEffect(dynamicExpr, [
|
||||
{
|
||||
type: IRNodeTypes.SET_DYNAMIC_PROPS,
|
||||
|
@ -128,17 +123,22 @@ function buildProps(
|
|||
},
|
||||
])
|
||||
} else {
|
||||
for (const result of results) {
|
||||
context.registerEffect(
|
||||
[result.value],
|
||||
[
|
||||
const irProps = dedupeProperties(results)
|
||||
for (const prop of irProps) {
|
||||
const { key, values } = prop
|
||||
if (key.isStatic && values.length === 1 && values[0].isStatic) {
|
||||
context.template += ` ${key.content}`
|
||||
if (values[0].content) context.template += `="${values[0].content}"`
|
||||
} else {
|
||||
const expressions = values.filter(v => !v.isStatic)
|
||||
context.registerEffect(expressions, [
|
||||
{
|
||||
type: IRNodeTypes.SET_PROP,
|
||||
element: context.reference(),
|
||||
prop: result,
|
||||
prop: prop,
|
||||
},
|
||||
],
|
||||
)
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,32 +147,27 @@ function transformProp(
|
|||
prop: VaporDirectiveNode | AttributeNode,
|
||||
node: ElementNode,
|
||||
context: TransformContext<ElementNode>,
|
||||
asDynamic: boolean,
|
||||
): DirectiveTransformResult | void {
|
||||
const { name } = prop
|
||||
if (isReservedProp(name)) return
|
||||
|
||||
if (prop.type === NodeTypes.ATTRIBUTE) {
|
||||
if (asDynamic) {
|
||||
return {
|
||||
key: createSimpleExpression(prop.name, true, prop.nameLoc),
|
||||
value: createSimpleExpression(
|
||||
prop.value ? prop.value.content : '',
|
||||
true,
|
||||
prop.value && prop.value.loc,
|
||||
),
|
||||
}
|
||||
} else {
|
||||
context.template += ` ${name}`
|
||||
if (prop.value) context.template += `="${prop.value.content}"`
|
||||
return
|
||||
return {
|
||||
key: createSimpleExpression(prop.name, true, prop.nameLoc),
|
||||
value: createSimpleExpression(
|
||||
prop.value ? prop.value.content : '',
|
||||
true,
|
||||
prop.value && prop.value.loc,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const directiveTransform = context.options.directiveTransforms[name]
|
||||
if (directiveTransform) {
|
||||
return directiveTransform(prop, node, context)
|
||||
} else if (!isBuiltInDirective(name)) {
|
||||
}
|
||||
|
||||
if (!isBuiltInDirective(name)) {
|
||||
context.registerOperation({
|
||||
type: IRNodeTypes.WITH_DIRECTIVE,
|
||||
element: context.reference(),
|
||||
|
@ -180,3 +175,42 @@ function transformProp(
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Dedupe props in an object literal.
|
||||
// Literal duplicated attributes would have been warned during the parse phase,
|
||||
// however, it's possible to encounter duplicated `onXXX` handlers with different
|
||||
// modifiers. We also need to merge static and dynamic class / style attributes.
|
||||
function dedupeProperties(results: DirectiveTransformResult[]): IRProp[] {
|
||||
const knownProps: Map<string, IRProp> = new Map()
|
||||
const deduped: IRProp[] = []
|
||||
|
||||
for (const result of results) {
|
||||
const prop = normalizeIRProp(result)
|
||||
// dynamic keys are always allowed
|
||||
if (!prop.key.isStatic) {
|
||||
deduped.push(prop)
|
||||
continue
|
||||
}
|
||||
const name = prop.key.content
|
||||
const existing = knownProps.get(name)
|
||||
if (existing) {
|
||||
if (name === 'style' || name === 'class') {
|
||||
mergeAsArray(existing, prop)
|
||||
}
|
||||
// unexpected duplicate, should have emitted error during parse
|
||||
} else {
|
||||
knownProps.set(name, prop)
|
||||
deduped.push(prop)
|
||||
}
|
||||
}
|
||||
return deduped
|
||||
}
|
||||
|
||||
function normalizeIRProp(prop: DirectiveTransformResult): IRProp {
|
||||
return extend({}, prop, { value: undefined, values: [prop.value] })
|
||||
}
|
||||
|
||||
function mergeAsArray(existing: IRProp, incoming: IRProp) {
|
||||
const newValues = incoming.values
|
||||
existing.values.push(...newValues)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue