Skip to content

Commit

Permalink
wip: support v-bind="{}"
Browse files Browse the repository at this point in the history
  • Loading branch information
ygj6 committed Feb 1, 2024
1 parent 7742b06 commit 89a1d87
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 225 deletions.
14 changes: 9 additions & 5 deletions packages/compiler-vapor/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import {
import { SourceMapGenerator } from 'source-map-js'
import { isString } from '@vue/shared'
import type { ParserPlugin } from '@babel/parser'
import { genSetArrProps, genSetObjProps, genSetProp } from './generators/prop'
import {
genMergeBatchProps,
genSetBatchProps,
genSetProp,
} from './generators/prop'
import { genCreateTextNode, genSetText } from './generators/text'
import { genSetEvent } from './generators/event'
import { genSetHtml } from './generators/html'
Expand Down Expand Up @@ -348,10 +352,10 @@ function genOperation(oper: OperationNode, context: CodegenContext) {
switch (oper.type) {
case IRNodeTypes.SET_PROP:
return genSetProp(oper, context)
case IRNodeTypes.SET_OBJ_PROPS:
return genSetObjProps(oper, context)
case IRNodeTypes.SET_ARR_PROPS:
return genSetArrProps(oper, context)
case IRNodeTypes.SET_BATCH_PROPS:
return genSetBatchProps(oper, context)
case IRNodeTypes.SET_MERGE_BATCH_PROPS:
return genMergeBatchProps(oper, context)
case IRNodeTypes.SET_TEXT:
return genSetText(oper, context)
case IRNodeTypes.SET_EVENT:
Expand Down
63 changes: 63 additions & 0 deletions packages/compiler-vapor/src/generators/objectExpression.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
NewlineType,
type ObjectExpression,
type SimpleExpressionNode,
type SourceLocation,
isSimpleIdentifier,
} from '@vue/compiler-dom'
import type { CodegenContext } from '../generate'
import { genExpression } from './expression'
import type { IRExpression } from '@vue/compiler-vapor'
import { isString } from '@vue/shared'

export function genObjectExpression(
node: ObjectExpression,
context: CodegenContext,
): void {
const { push, withIndent, newline } = context
const { properties, loc } = node
if (!properties.length) {
push(`{}`, NewlineType.None, loc)
}
const multilines = properties.length > 1
push(multilines ? `{` : `{ `)
const genProperties = () => {
for (let i = 0; i < properties.length; i++) {
const { key, value, loc } = properties[i]
// key
genExpressionAsPropertyKey(key as SimpleExpressionNode, loc, context)
push(': ')
// value
genExpression(value as SimpleExpressionNode, context)
if (i < properties.length - 1) {
// will only reach this if it's multilines
push(',')
newline()
}
}
}
multilines
? withIndent(() => {
newline()
genProperties()
newline()
})
: genProperties()
push(multilines ? '}' : ' }')
}

function genExpressionAsPropertyKey(
node: IRExpression,
loc: SourceLocation,
context: CodegenContext,
) {
const { push, pushMulti } = context
if (isString(node) || node.isStatic) {
const keyName = isString(node) ? node : node.content
// only quote keys if necessary
const text = isSimpleIdentifier(keyName) ? keyName : JSON.stringify(keyName)
push(text, NewlineType.None, loc)
} else {
pushMulti(['[', ']'], () => genExpression(node, context))
}
}
151 changes: 59 additions & 92 deletions packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
import type { CodegenContext } from '../generate'
import type { SetArrPropsIRNode, SetObjPropsIRNode, SetPropIRNode } from '../ir'
import type {
SetBatchPropsIRNode,
SetMergeBatchPropsIRNode,
SetPropIRNode,
} from '../ir'
import { genExpression } from './expression'
import { isString } from '@vue/shared'
import { NewlineType } from '@vue/compiler-core'
import {
type ObjectExpression,
type SourceLocation,
createObjectExpression,
createObjectProperty,
} from '@vue/compiler-core'
import { genObjectExpression } from './objectExpression'
import type { DirectiveTransformResult } from '../transform'

export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
const { pushFnCall, newline, vaporHelper } = context

newline()

const element = `n${oper.element}`

// fast path for static props
if (isString(oper.key) || oper.key.isStatic) {
const keyName = isString(oper.key) ? oper.key : oper.key.content
for (const { key, value, modifier } of oper.value) {
const keyName = isString(key) ? key : key.content

let helperName: string | undefined
let omitKey = false
Expand All @@ -23,109 +31,68 @@ export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
} else if (keyName === 'style') {
helperName = 'setStyle'
omitKey = true
} else if (oper.modifier) {
helperName = oper.modifier === '.' ? 'setDOMProp' : 'setAttr'
} else if (modifier) {
helperName = modifier === '.' ? 'setDOMProp' : 'setAttr'
}

if (helperName) {
pushFnCall(
vaporHelper(helperName),
element,
omitKey
? false
: () => {
const expr = () => genExpression(oper.key, context)
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), expr)
} else {
expr()
}
},
() => genExpression(oper.value, context),
`n${oper.element}`,
omitKey ? false : () => genExpression(key, context),
() => genExpression(value, context),
)
return
}
}

pushFnCall(
vaporHelper('setDynamicProp'),
element,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
} else if (oper.modifier) {
pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
genExpression(oper.key, context),
)
} else {
genExpression(oper.key, context)
}
},
() => genExpression(oper.value, context),
)
}

export function genSetObjProps(
oper: SetObjPropsIRNode,
export function genSetBatchProps(
oper: SetBatchPropsIRNode,
context: CodegenContext,
) {
const { push, pushFnCall, newline, vaporHelper } = context
const { pushFnCall, newline, vaporHelper } = context

newline()

pushFnCall(vaporHelper('setObjProps'), `n${oper.element}`, () => {
// TODO genObjectExpression
if (!oper.value) {
push('{}', NewlineType.None, oper.loc)
}
push('{')
for (let i = 0; i < oper.value.length; i++) {
const { key, value } = oper.value[i]
push('[')
genExpression(key, context)
push(']:')
genExpression(value, context)
push(',')
}
push('}')
})
pushFnCall(vaporHelper('setBatchProps'), `n${oper.element}`, () =>
genLiteralObjectProp(oper.value, oper.loc, context),
)
}

export function genSetArrProps(
oper: SetArrPropsIRNode,
export function genMergeBatchProps(
oper: SetMergeBatchPropsIRNode,
context: CodegenContext,
) {
const { push, pushFnCall, newline, vaporHelper } = context
const { pushMulti, pushFnCall, newline, vaporHelper } = context

newline()
pushFnCall(
vaporHelper('setArrProps'),
`n${oper.element}`,
() => {
// TODO genArrayExpression
push('[')
for (let i = 0; i < oper.value.length; i++) {
const props = oper.value[i]
for (const prop of props) {
// TODO genObjectExpression
if ('key' in prop) {
push('{')
const { key, value } = prop
push('[')
genExpression(key, context)
push(']:')
genExpression(value, context)
push(',')
push('}')
} else {
genExpression(prop, context)
}
push(',')
}
}
push(']')
},
`${oper.needMerge}`,
)
pushFnCall(vaporHelper('mergeBatchProps'), `n${oper.element}`, () => {
pushMulti(
['[', ']', ', '],
// props to be merged
...oper.value.map(prop => () => {
Array.isArray(prop)
? genLiteralObjectProp(prop, oper.loc, context)
: genExpression(prop, context)
}),
)
})
}

function genLiteralObjectProp(
prop: DirectiveTransformResult[],
loc: SourceLocation,
context: CodegenContext,
) {
const { helper } = context
const properties: ObjectExpression['properties'] = []
for (const { key, value, runtimeCamelize, modifier } of prop) {
if (runtimeCamelize) {
key.content = `${helper('camelize')}(${key.content})`
} else if (modifier) {
key.content = `${modifier}${key.content}`
}
properties.push(createObjectProperty(key, value))
}
genObjectExpression(createObjectExpression(properties, loc), context)
}
33 changes: 17 additions & 16 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,20 @@ import type {
TemplateChildNode,
} from '@vue/compiler-dom'
import type { Prettify } from '@vue/shared'
import type { DirectiveTransform, NodeTransform } from './transform'
import type {
DirectiveTransform,
DirectiveTransformResult,
NodeTransform,
} from './transform'

export enum IRNodeTypes {
ROOT,
TEMPLATE_FACTORY,
FRAGMENT_FACTORY,

SET_PROP,
SET_OBJ_PROPS,
SET_ARR_PROPS,
SET_BATCH_PROPS,
SET_MERGE_BATCH_PROPS,
SET_TEXT,
SET_EVENT,
SET_HTML,
Expand Down Expand Up @@ -79,23 +83,20 @@ export interface FragmentFactoryIRNode extends BaseIRNode {
export interface SetPropIRNode extends BaseIRNode {
type: IRNodeTypes.SET_PROP
element: number
key: IRExpression
value: IRExpression
modifier?: '.' | '^'
runtimeCamelize: boolean
value: DirectiveTransformResult[]
}

export interface SetObjPropsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_OBJ_PROPS
export interface SetBatchPropsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_BATCH_PROPS
element: number
value: any
value: DirectiveTransformResult[]
}

export interface SetArrPropsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_ARR_PROPS
export type PropsExpression = DirectiveTransformResult[] | IRExpression
export interface SetMergeBatchPropsIRNode extends BaseIRNode {
type: IRNodeTypes.SET_MERGE_BATCH_PROPS
element: number
value: any
needMerge: boolean
value: PropsExpression[]
}

export interface SetTextIRNode extends BaseIRNode {
Expand Down Expand Up @@ -181,8 +182,8 @@ export type IRNode =
| FragmentFactoryIRNode
export type OperationNode =
| SetPropIRNode
| SetObjPropsIRNode
| SetArrPropsIRNode
| SetBatchPropsIRNode
| SetMergeBatchPropsIRNode
| SetTextIRNode
| SetEventIRNode
| SetHtmlIRNode
Expand Down
10 changes: 7 additions & 3 deletions packages/compiler-vapor/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import {
NodeTypes,
type ParentNode,
type RootNode,
type SimpleExpressionNode,
type SourceLocation,
type TemplateChildNode,
defaultOnError,
defaultOnWarn, type Property,
defaultOnWarn,
isVSlot,
} from '@vue/compiler-dom'
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
Expand Down Expand Up @@ -39,9 +41,11 @@ export type DirectiveTransform = (
) => void | DirectiveTransformResult

export interface DirectiveTransformResult {
props: Property[]
key: SimpleExpressionNode
value: SimpleExpressionNode
loc: SourceLocation
modifier?: '.' | '^'
runtimeCamelize: boolean
runtimeCamelize?: boolean
}

// A structural directive transform is technically also a NodeTransform;
Expand Down

0 comments on commit 89a1d87

Please sign in to comment.