- [x] `v-bind`
- [x] simple expression
- [x] compound expression
- - [ ] modifiers
+ - [x] modifiers
- [x] .camel
- - [ ] .prop
- - [ ] .attr
+ - [x] .prop
+ - [x] .attr
- [x] `v-on`
- [x] simple expression
- [x] compound expression
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler v-bind > .attr modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)
+ _setAttr(n1, "foo-bar", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)
+ _setAttr(n1, "foo-bar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.id)
})
return n0
}"
`;
exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
-"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
+"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div></div>")
const n0 = t0()
const { 0: [n1],} = _children(n0)
_renderEffect(() => {
- _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
+ _setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
})
return n0
}"
`;
exports[`compiler: v-once > basic 1`] = `
-"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor';
+"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend } from 'vue/vapor';
export function render(_ctx) {
const t0 = _template("<div> <span></span></div>")
const { 0: [n3, { 1: [n2],}],} = _children(n0)
const n1 = _createTextNode(_ctx.msg)
_setText(n1, undefined, _ctx.msg)
- _setDynamicProp(n2, "class", undefined, _ctx.clz)
+ _setClass(n2, "class", undefined, _ctx.clz)
_prepend(n3, n1)
return n0
}"
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: undefined,
})
expect(code).matchSnapshot()
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: undefined,
})
expect(code).matchSnapshot()
const { ir, code } = compileWithVBind(`<div v-bind:[foo].camel="id"/>`)
expect(ir.effect[0].operations[0]).toMatchObject({
- runtimeCamelize: true,
key: {
content: `foo`,
isStatic: false,
content: `id`,
isStatic: false,
},
+ runtimeCamelize: true,
+ modifier: undefined,
})
expect(code).matchSnapshot()
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
})
test('.prop modifier w/ no expression', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
})
test('.prop modifier w/ dynamic arg', () => {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
})
test('.prop modifier (shortband) w/ no expression', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `.fooBar`,
+ content: `fooBar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '.',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
})
test('.attr modifier', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `^foo-bar`,
+ content: `foo-bar`,
isStatic: true,
},
value: {
content: `id`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '^',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)')
+ expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.id)')
})
test('.attr modifier w/ no expression', () => {
expect(ir.effect[0].operations[0]).toMatchObject({
key: {
- content: `^foo-bar`,
+ content: `foo-bar`,
isStatic: true,
},
value: {
content: `fooBar`,
isStatic: false,
},
+ runtimeCamelize: false,
+ modifier: '^',
})
expect(code).matchSnapshot()
expect(code).contains('renderEffect')
- expect(code).contains(
- '_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)',
- )
+ expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.fooBar)')
})
})
import type { CodegenContext } from '../generate'
import type { SetPropIRNode } from '../ir'
import { genExpression } from './expression'
+import { isString } from '@vue/shared'
export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
const { pushFnCall, pushMulti, newline, vaporHelper, helper } = 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
+
+ let helperName: string | undefined
+ if (keyName === 'class') {
+ helperName = 'setClass'
+ } else if (keyName === 'style') {
+ helperName = 'setStyle'
+ } else if (oper.modifier) {
+ helperName = oper.modifier === '.' ? 'setDOMProp' : 'setAttr'
+ }
+
+ if (helperName) {
+ pushFnCall(
+ vaporHelper(helperName),
+ element,
+ () => {
+ const expr = () => genExpression(oper.key, context)
+ if (oper.runtimeCamelize) {
+ pushFnCall(helper('camelize'), expr)
+ } else {
+ expr()
+ }
+ },
+ 'undefined',
+ () => genExpression(oper.value, context),
+ )
+ return
+ }
+ }
+
pushFnCall(
vaporHelper('setDynamicProp'),
- `n${oper.element}`,
+ element,
// 2. key name
() => {
if (oper.runtimeCamelize) {
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
- } else if (oper.runtimePrefix) {
- pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () =>
+ } else if (oper.modifier) {
+ pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
genExpression(oper.key, context),
)
} else {
element: number
key: IRExpression
value: IRExpression
+ modifier?: '.' | '^'
runtimeCamelize: boolean
- runtimePrefix?: string
}
export interface SetTextIRNode extends BaseIRNode {
}
}
- let prefix: string | undefined
- if (modifiers.includes('prop')) {
- prefix = injectPrefix(arg, '.')
- }
- if (modifiers.includes('attr')) {
- prefix = injectPrefix(arg, '^')
- }
-
if (!exp.content.trim()) {
context.options.onError(
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
key: arg,
value: exp,
runtimeCamelize: camel,
- runtimePrefix: prefix,
+ modifier: modifiers.includes('prop')
+ ? '.'
+ : modifiers.includes('attr')
+ ? '^'
+ : undefined,
},
],
)
}
-
-const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => {
- if (!arg.isStatic) {
- return prefix
- }
- arg.content = prefix + arg.content
-}