Skip to content

Commit 7742b06

Browse files
committed
wip: support v-bind="{}"
1 parent d7ab873 commit 7742b06

File tree

8 files changed

+330
-34
lines changed

8 files changed

+330
-34
lines changed

packages/compiler-vapor/src/generate.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import {
2020
import { SourceMapGenerator } from 'source-map-js'
2121
import { isString } from '@vue/shared'
2222
import type { ParserPlugin } from '@babel/parser'
23-
import { genSetProp } from './generators/prop'
23+
import { genSetArrProps, genSetObjProps, genSetProp } from './generators/prop'
2424
import { genCreateTextNode, genSetText } from './generators/text'
2525
import { genSetEvent } from './generators/event'
2626
import { genSetHtml } from './generators/html'
@@ -348,6 +348,10 @@ function genOperation(oper: OperationNode, context: CodegenContext) {
348348
switch (oper.type) {
349349
case IRNodeTypes.SET_PROP:
350350
return genSetProp(oper, context)
351+
case IRNodeTypes.SET_OBJ_PROPS:
352+
return genSetObjProps(oper, context)
353+
case IRNodeTypes.SET_ARR_PROPS:
354+
return genSetArrProps(oper, context)
351355
case IRNodeTypes.SET_TEXT:
352356
return genSetText(oper, context)
353357
case IRNodeTypes.SET_EVENT:

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

+66-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { CodegenContext } from '../generate'
2-
import type { SetPropIRNode } from '../ir'
2+
import type { SetArrPropsIRNode, SetObjPropsIRNode, SetPropIRNode } from '../ir'
33
import { genExpression } from './expression'
44
import { isString } from '@vue/shared'
5+
import { NewlineType } from '@vue/compiler-core'
56

67
export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
78
const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
@@ -64,3 +65,67 @@ export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
6465
() => genExpression(oper.value, context),
6566
)
6667
}
68+
69+
export function genSetObjProps(
70+
oper: SetObjPropsIRNode,
71+
context: CodegenContext,
72+
) {
73+
const { push, pushFnCall, newline, vaporHelper } = context
74+
75+
newline()
76+
77+
pushFnCall(vaporHelper('setObjProps'), `n${oper.element}`, () => {
78+
// TODO genObjectExpression
79+
if (!oper.value) {
80+
push('{}', NewlineType.None, oper.loc)
81+
}
82+
push('{')
83+
for (let i = 0; i < oper.value.length; i++) {
84+
const { key, value } = oper.value[i]
85+
push('[')
86+
genExpression(key, context)
87+
push(']:')
88+
genExpression(value, context)
89+
push(',')
90+
}
91+
push('}')
92+
})
93+
}
94+
95+
export function genSetArrProps(
96+
oper: SetArrPropsIRNode,
97+
context: CodegenContext,
98+
) {
99+
const { push, pushFnCall, newline, vaporHelper } = context
100+
101+
newline()
102+
pushFnCall(
103+
vaporHelper('setArrProps'),
104+
`n${oper.element}`,
105+
() => {
106+
// TODO genArrayExpression
107+
push('[')
108+
for (let i = 0; i < oper.value.length; i++) {
109+
const props = oper.value[i]
110+
for (const prop of props) {
111+
// TODO genObjectExpression
112+
if ('key' in prop) {
113+
push('{')
114+
const { key, value } = prop
115+
push('[')
116+
genExpression(key, context)
117+
push(']:')
118+
genExpression(value, context)
119+
push(',')
120+
push('}')
121+
} else {
122+
genExpression(prop, context)
123+
}
124+
push(',')
125+
}
126+
}
127+
push(']')
128+
},
129+
`${oper.needMerge}`,
130+
)
131+
}

packages/compiler-vapor/src/ir.ts

+17
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export enum IRNodeTypes {
1616
FRAGMENT_FACTORY,
1717

1818
SET_PROP,
19+
SET_OBJ_PROPS,
20+
SET_ARR_PROPS,
1921
SET_TEXT,
2022
SET_EVENT,
2123
SET_HTML,
@@ -83,6 +85,19 @@ export interface SetPropIRNode extends BaseIRNode {
8385
runtimeCamelize: boolean
8486
}
8587

88+
export interface SetObjPropsIRNode extends BaseIRNode {
89+
type: IRNodeTypes.SET_OBJ_PROPS
90+
element: number
91+
value: any
92+
}
93+
94+
export interface SetArrPropsIRNode extends BaseIRNode {
95+
type: IRNodeTypes.SET_ARR_PROPS
96+
element: number
97+
value: any
98+
needMerge: boolean
99+
}
100+
86101
export interface SetTextIRNode extends BaseIRNode {
87102
type: IRNodeTypes.SET_TEXT
88103
element: number
@@ -166,6 +181,8 @@ export type IRNode =
166181
| FragmentFactoryIRNode
167182
export type OperationNode =
168183
| SetPropIRNode
184+
| SetObjPropsIRNode
185+
| SetArrPropsIRNode
169186
| SetTextIRNode
170187
| SetEventIRNode
171188
| SetHtmlIRNode

packages/compiler-vapor/src/transform.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type RootNode,
1010
type TemplateChildNode,
1111
defaultOnError,
12-
defaultOnWarn,
12+
defaultOnWarn, type Property,
1313
isVSlot,
1414
} from '@vue/compiler-dom'
1515
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
@@ -36,7 +36,13 @@ export type DirectiveTransform = (
3636
dir: VaporDirectiveNode,
3737
node: ElementNode,
3838
context: TransformContext<ElementNode>,
39-
) => void
39+
) => void | DirectiveTransformResult
40+
41+
export interface DirectiveTransformResult {
42+
props: Property[]
43+
modifier?: '.' | '^'
44+
runtimeCamelize: boolean
45+
}
4046

4147
// A structural directive transform is technically also a NodeTransform;
4248
// Only v-if and v-for fall into this category.

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

+117-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
import {
22
type AttributeNode,
3+
createCompilerError,
34
type ElementNode,
45
ElementTypes,
6+
ErrorCodes,
7+
ExpressionNode,
8+
isStaticExp,
59
NodeTypes,
10+
type ObjectExpression,
611
} from '@vue/compiler-dom'
712
import { isBuiltInDirective, isReservedProp, isVoidTag } from '@vue/shared'
8-
import type { NodeTransform, TransformContext } from '../transform'
13+
import {
14+
DirectiveTransformResult,
15+
NodeTransform,
16+
TransformContext,
17+
} from '../transform'
918
import { IRNodeTypes, type VaporDirectiveNode } from '../ir'
1019

1120
export const transformElement: NodeTransform = (node, ctx) => {
@@ -49,16 +58,120 @@ function buildProps(
4958
props: ElementNode['props'] = node.props,
5059
isComponent: boolean,
5160
) {
61+
const expressions = []
62+
const properties: ObjectExpression['properties'] = []
63+
const mergeArgs: ExpressionNode[] = []
64+
65+
const pushMergeArg = (arg?: ExpressionNode) => {
66+
if (properties.length) {
67+
// TODO dedupe properties
68+
mergeArgs.push([...properties])
69+
properties.length = 0
70+
}
71+
}
72+
5273
for (const prop of props) {
53-
transformProp(prop as VaporDirectiveNode | AttributeNode, node, context)
74+
if (prop.type === NodeTypes.DIRECTIVE) {
75+
const isVBind = prop.name === 'bind'
76+
if (!prop.arg && isVBind) {
77+
if (prop.exp) {
78+
if (isVBind) {
79+
pushMergeArg()
80+
expressions.push(prop.exp)
81+
mergeArgs.push([prop.exp])
82+
}
83+
} else {
84+
context.options.onError(
85+
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, prop.loc),
86+
)
87+
}
88+
continue
89+
}
90+
}
91+
92+
const result = transformProp(
93+
prop as VaporDirectiveNode | AttributeNode,
94+
node,
95+
context,
96+
)
97+
if (result) {
98+
const { props: propsObj } = result
99+
for (const prop of propsObj) {
100+
!isStaticExp(prop.key) && expressions.push(prop.key)
101+
!isStaticExp(prop.value) && expressions.push(prop.value)
102+
}
103+
properties.push(...propsObj)
104+
}
105+
}
106+
107+
if (mergeArgs.length) {
108+
pushMergeArg()
109+
if (mergeArgs.length > 1) {
110+
context.registerEffect(expressions, [
111+
{
112+
type: IRNodeTypes.SET_ARR_PROPS,
113+
loc: node.loc,
114+
element: context.reference(),
115+
value: mergeArgs,
116+
needMerge: true,
117+
},
118+
])
119+
} else {
120+
context.registerEffect(expressions, [
121+
{
122+
type: IRNodeTypes.SET_ARR_PROPS,
123+
loc: node.loc,
124+
element: context.reference(),
125+
value: mergeArgs,
126+
needMerge: false,
127+
},
128+
])
129+
}
130+
} else if (properties.length) {
131+
let hasDynamicKey = false
132+
for (let i = 0; i < properties.length; i++) {
133+
const key = properties[i].key
134+
if (isStaticExp(key)) {
135+
// todo
136+
} else if (!key.isHandlerKey) {
137+
hasDynamicKey = true
138+
}
139+
}
140+
if (!hasDynamicKey) {
141+
// TODO handle class/style prop
142+
for (const prop of properties) {
143+
context.registerEffect(
144+
[prop.value],
145+
[
146+
{
147+
...prop,
148+
type: IRNodeTypes.SET_PROP,
149+
loc: prop.loc,
150+
element: context.reference(),
151+
key: prop.key,
152+
value: prop.value,
153+
},
154+
],
155+
)
156+
}
157+
} else {
158+
context.registerEffect(expressions, [
159+
{
160+
type: IRNodeTypes.SET_OBJ_PROPS,
161+
loc: node.loc,
162+
element: context.reference(),
163+
value: properties,
164+
},
165+
])
166+
}
54167
}
55168
}
56169

57170
function transformProp(
58171
prop: VaporDirectiveNode | AttributeNode,
59172
node: ElementNode,
60173
context: TransformContext<ElementNode>,
61-
): void {
174+
): void | DirectiveTransformResult {
62175
const { name, loc } = prop
63176
if (isReservedProp(name)) return
64177

@@ -70,7 +183,7 @@ function transformProp(
70183

71184
const directiveTransform = context.options.directiveTransforms[name]
72185
if (directiveTransform) {
73-
directiveTransform(prop, node, context)
186+
return directiveTransform(prop, node, context)
74187
} else if (!isBuiltInDirective(name)) {
75188
context.registerOperation({
76189
type: IRNodeTypes.WITH_DIRECTIVE,

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

+14-24
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import {
55
createSimpleExpression,
66
} from '@vue/compiler-dom'
77
import { camelize, isReservedProp } from '@vue/shared'
8-
import { IRNodeTypes } from '../ir'
98
import type { DirectiveTransform } from '../transform'
109

1110
export function normalizeBindShorthand(
@@ -19,12 +18,9 @@ export function normalizeBindShorthand(
1918
}
2019

2120
export const transformVBind: DirectiveTransform = (dir, node, context) => {
22-
let { arg, exp, loc, modifiers } = dir
21+
let { exp, loc, modifiers } = dir
22+
const arg = dir.arg!
2323

24-
if (!arg) {
25-
// TODO support v-bind="{}"
26-
return
27-
}
2824
if (arg.isStatic && isReservedProp(arg.content)) return
2925

3026
if (!exp) exp = normalizeBindShorthand(arg)
@@ -46,22 +42,16 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
4642
return
4743
}
4844

49-
context.registerEffect(
50-
[exp],
51-
[
52-
{
53-
type: IRNodeTypes.SET_PROP,
54-
loc: dir.loc,
55-
element: context.reference(),
56-
key: arg,
57-
value: exp,
58-
runtimeCamelize: camel,
59-
modifier: modifiers.includes('prop')
60-
? '.'
61-
: modifiers.includes('attr')
62-
? '^'
63-
: undefined,
64-
},
65-
],
66-
)
45+
return {
46+
props: [{
47+
key: arg,
48+
value: exp,
49+
runtimeCamelize: camel,
50+
modifier: modifiers.includes('prop')
51+
? '.'
52+
: modifiers.includes('attr')
53+
? '^'
54+
: undefined,
55+
}]
56+
}
6757
}

0 commit comments

Comments
 (0)